Browse Source

- initial import of the 'binrpc' module, an evolution of the 'ctl' existing
module;
- uses the binrpc2 library;
- adds parallelism (for all supported transports);
- drops the FIFO transport (as being both slow and unconfortable); it was left
out due to design considerations, but, if proved to be necessary, could
probably be added back

bpi 15 years ago
parent
commit
1e7124dfcf

+ 19 - 0
modules/binrpc/Makefile

@@ -0,0 +1,19 @@
+# $Id$
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+
+NAME=binrpc.so
+
+DEFS+=-DSER_MOD_INTERFACE
+
+DEFS+=-I$(SERLIBPATH)/binrpc2/
+DEFS+=-I../../
+
+
+SER_LIBS+=$(SERLIBPATH)/binrpc2/binrpc2
+
+include ../../Makefile.modules
+

+ 14 - 0
modules/binrpc/TODO

@@ -0,0 +1,14 @@
+MOD:
+[ ] floating points
+[ ] time_t -> ISO|UTC?
+[ ] support for FIFO
+[*] multiple workers
+[ ] reply (200/OK) on no reply
+[ ] fault sending if rpl too big
+[ ] lists and AVPs
+[*] use pkg
+[ ] use timers
+
+IF'ACE:
+[ ] extend descriptor support / handling of values (add maps, lists, AVPs[?])
+[ ] add NULL support

+ 381 - 0
modules/binrpc/binrpc_mod.c

@@ -0,0 +1,381 @@
+/*
+ * $Id: ctl.c,v 1.3 2007/01/18 20:35:01 andrei Exp $
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ * 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:
+ *    [email protected]
+ *
+ * ser 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
+ */
+/* History:
+ * --------
+ *  2006-02-08  created by andrei
+ *  2007-01-18  use PROC_RPC rank when forking (andrei)
+ *  2007        ported to libbinrpc (bpintea)
+ */
+
+
+
+#include "../../sr_module.h"
+#include "../../ut.h"
+#include "../../dprint.h"
+#include "../../pt.h"
+#include "ctrl_socks.h"
+#include "io_listener.h"
+
+#include <sys/types.h>
+#include <sys/socket.h> /* socketpair */
+#include <unistd.h> /* close, fork, getpid */
+#include <stdio.h> /* snprintf */
+#include <string.h> /* strerror */
+#include <signal.h> /* kill */
+#include <errno.h>
+
+#include <binrpc.h>
+#include "../../mem/wrappers.h"
+
+MODULE_VERSION
+
+#include "modbrpc_defaults.h"
+#include "libbinrpc_wrapper.h"
+#include "proctab.h"
+#ifdef USE_FIFO
+#include "fifo_server.h"
+#endif
+
+static int mod_init(void);
+static int mod_child(int rank);
+static void mod_destroy(void);
+
+static cmd_export_t cmds[]={
+		{0,0,0,0,0}
+};
+
+
+static int usock_mode=0600; /* permissions, default rw-------*/
+static int usock_uid=-1; /* username and group for the unix sockets*/
+static int usock_gid=-1;
+static int workers = 1; /* how many processes to start */
+
+static int append_binrpc_listener(modparam_t type, void * val);
+#ifdef USE_FIFO
+static int append_fifo_socket(modparam_t type, void * val);
+#endif
+static int fix_user(modparam_t type, void * val);
+static int fix_group(modparam_t type, void * val);
+
+
+void io_listen_who_rpc(rpc_t* rpc, void* ctx);
+void io_listen_conn_rpc(rpc_t* rpc, void* ctx);
+#ifdef EXTRA_DEBUG
+static void ctrl_listen_pid(rpc_t *rpc, void *ctx);
+#endif
+
+
+static char* io_listen_who_doc[]={ "list open connections", 0 };
+static char* io_listen_conn_doc[]={ "returns number of open connections", 0 };
+#ifdef EXTRA_DEBUG
+static char *ctrl_listen_pid_doc[] = {"list serving's process PID", NULL};
+#endif
+
+static rpc_export_t ctl_rpc[]={
+	{"binrpc.who",         io_listen_who_rpc, (const char**)io_listen_who_doc,
+			0},
+	{"binrpc.connections", io_listen_conn_rpc,(const char**)io_listen_conn_doc,
+			0},
+	{"binrpc.listen",      ctrl_listen_ls_rpc,(const char**)ctl_listen_ls_doc,
+			0},
+#ifdef EXTRA_DEBUG
+	{"binrpc.pid", ctrl_listen_pid, (const char **)ctrl_listen_pid_doc, 0},
+#endif
+	{ 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+#ifdef USE_FIFO
+	{"fifo",	PARAM_STRING|PARAM_USE_FUNC,	(void*)append_fifo_socket},
+#endif
+	{"listen",	PARAM_STRING|PARAM_USE_FUNC,	(void*)append_binrpc_listener},
+	{"mode",	PARAM_INT,						&usock_mode				 },
+	{"user",	PARAM_STRING|PARAM_USE_FUNC,	fix_user				 },
+	{"group",	PARAM_STRING|PARAM_USE_FUNC,	fix_group				 },
+	{"workers",	PARAM_INT,						&workers},
+	{"force_reply", PARAM_INT,					&force_reply},
+	{0,0,0} 
+}; /* no params */
+
+struct module_exports exports= {
+	"binrpc",
+	cmds,
+	ctl_rpc,        /* RPC methods */
+	params,
+	mod_init, /* module initialization function */
+	0, /* response function */
+	mod_destroy,  /* destroy function */
+	0, /* on_cancel function */
+	mod_child, /* per-child init function */
+};
+
+static char **sock_paths = NULL;
+
+
+static int append_binrpc_listener(modparam_t type, void * val)
+{
+	char *s;
+	
+	if ((type & PARAM_STRING)==0){
+		BUG("binrpc: bad parameter type %d (expected string).\n", type);
+		return -1;
+	}
+	s=(char*)val;
+	return add_binrpc_listener(s);
+}
+
+
+#ifdef USE_FIFO
+static int append_fifo_socket(modparam_t type, void * val)
+{
+	char *s;
+	
+	if ((type & PARAM_STRING)==0){
+		BUG("add_fifo: bad parameter type %d (expected string).\n", type);
+		return -1;
+	}
+	s=(char*)val;
+	return add_fifo_socket(s);
+}
+#endif /* USE_FIFO */
+
+
+
+static int fix_user(modparam_t type, void * val)
+{
+	char* s;
+	
+	if ((type & PARAM_STRING)==0){
+		BUG("fix_user: bad parameter type %d\n", type);
+		goto error;
+	}
+	s=(char*)val;
+	if (user2uid(&usock_uid, 0, s)<0){
+		ERR("bad user name/uid number %s\n", s);
+		goto error;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+
+static int fix_group(modparam_t type, void * val)
+{
+	char* s;
+	
+	if ((type & PARAM_STRING)==0){
+		BUG("fix_group: bad parameter type %d\n", type);
+		goto error;
+	}
+	s=(char*)val;
+	if (group2gid(&usock_gid, s)<0){
+		ERR("bad group name/gid number %s\n", s);
+		goto error;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+
+static int mod_init(void)
+{
+	struct ctrl_socket *csock;
+	char *sock_proto_name;
+	int have_stream = 0;
+	
+	brpc_mem_setup(w_pkg_calloc, w_pkg_malloc, w_pkg_free, w_pkg_realloc);
+
+	if (ctrl_sock_lst==0) {
+		add_binrpc_listener(DEFAULT_MODBRPC_SOCKET);
+	}
+	DEBUG("BINRPC listening on:\n");
+	for (csock = ctrl_sock_lst; csock; csock = csock->next) {
+		sock_proto_name = socket_proto_name(&csock->addr);
+		DEBUG("        [%s:%s]  %s\n", 
+#ifdef USE_FIFO
+				payload_proto_name(csock->p_proto),
+#else
+				payload_proto_name(P_BINRPC),
+#endif
+				sock_proto_name, csock->name);
+
+		/* initialize now all sockets. UDP receivers will be shut down later,
+		 * if using TCP receiver */
+		if (init_ctrl_socket(csock, usock_mode, usock_uid, usock_gid) < 0) {
+			ERR("socket initialization for %s failed (%s).\n", 
+					csock->name, strerror(errno));
+			return -1;
+		}
+		if (BRPC_ADDR_TYPE(&csock->addr) == SOCK_STREAM)
+			have_stream ++;
+	}
+	
+	if (ctrl_sock_lst){
+		/* will we be creating an extra process: conn. listener? */
+		have_connection_listener = (1 < workers) && (0 < have_stream);
+		/* we will fork */
+		register_procs(workers + 
+				((have_connection_listener) ? /*listener*/1 : 0));
+
+		if (! (sock_paths = ctrl_sock_paths()))
+			/* at least NULL maker must be present */
+			return -1;
+	}
+
+#ifdef USE_FIFO
+	fifo_rpc_init();
+#endif
+	return 0;
+}
+
+void release_sock_paths(int do_unlink)
+{
+	char **path;
+	if (! sock_paths)
+		/* not MAIN */
+		return;
+	for (path = &sock_paths[0]; *path; path ++) {
+		if (do_unlink && (unlink(*path) < 0))
+			WARN("failed to delete unix socket `%s': %s [%d].\n",
+					*path, strerror(errno), errno);
+		free(*path);
+	}
+	free(sock_paths);
+	sock_paths = NULL;
+}
+
+
+static int fork_workers(void)
+{
+	int i;
+	pid_t pid;
+	int sockpair[2];
+
+	if (have_connection_listener) {
+		if (pt_init(workers) < 0)
+			return -1;
+	}
+	for (i = 0; i < workers; i ++) {
+		if (have_connection_listener) {
+			if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockpair) < 0) {
+				ERR("failed to create socket pair: %s [%d].\n", 
+						strerror(errno), errno);
+				return -1;
+			}
+		}
+		switch ((pid = fork_process(PROC_RPC, "binrpc worker", 
+					/* support for SIP TCP */1))) {
+			case 0:  /* child */
+				if (have_connection_listener) {
+					/* close only stream listeners; they must not be removed as
+					 * will be ref'ed with connection file descriptors passed
+					 * by connection listener  */
+					close_binrpc_listeners(SOCKLIST_DGRAM);
+					/* don't need proc. table in workers */
+					pt_free(/* don't have CSs initialized */0);
+					close(sockpair[0]); /* parent's end */
+					if (! add_fdpass_socket(sockpair[1], pid))
+						return -1;
+				}
+				io_listen_loop(ctrl_sock_lst);
+				return -1;
+			case -1: /*err*/
+				ERR("failed to spawn BINRPC worker: %s [%d].\n", 
+						strerror(errno), errno);
+				return -1;
+			default: /*parent*/
+				DEBUG("new BINRPC worker spawned; pid: %d.\n", pid);
+				if (have_connection_listener) {
+					close(sockpair[1]); /* child's end */
+					if (pt_ins(pid, sockpair[0]) < 0)
+						return -1;
+				}
+		}
+	}
+	if (have_connection_listener) {
+		switch ((pid = fork_process(PROC_RPC, "binrpc connection listener", 
+				/* won't use SIP TCP */0))) {
+			case 0: /*child*/
+				/* remove datagram listeners */
+				DEBUG("removing dgram listeners from conn listener worker.\n");
+				del_binrpc_listeners(SOCKLIST_STREAM|SOCKLIST_FDPASS);
+				if (pt_fd2cs() < 0)
+					return -1;
+				io_listen_loop(ctrl_sock_lst);
+				return 0; /* formal */
+			case -1: /* err */
+				ERR("failed to spawn binrpc connection listener: %s [%d].\n",
+						strerror(errno), errno);
+				return -1;
+			default: /* parent */
+				DEBUG("BINRPC stream receiver spawned; pid: %d.\n", pid);
+		}
+	}
+	return 0;
+}
+
+static int mod_child(int rank)
+{
+	/* do nothing from PROC_INIT, is the same as PROC_MAIN */
+	if (rank==PROC_INIT)
+		return 0;
+	/* we want to fork(), but only from one process */
+	if ((rank == PROC_MAIN ) && (ctrl_sock_lst)){
+		DEBUG("mod_child(%d), ctrl_sock_lst=%p\n", rank, ctrl_sock_lst);
+		if (fork_workers() < 0)
+			goto error;
+	}
+
+	if (rank!=PROC_RPC){
+		/* close all the opened fds, we don't need them here */
+		DEBUG("removing BINRPC listeners from non RPC worker.\n");
+		del_binrpc_listeners(SOCKLIST_NONE);
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+static void mod_destroy(void)
+{
+	DEBUG("destroying BINRPC listeners.\n");
+	del_binrpc_listeners(SOCKLIST_NONE);
+	release_sock_paths(is_main);
+}
+
+
+#ifdef EXTRA_DEBUG
+static void ctrl_listen_pid(rpc_t *rpc, void *ctx)
+{
+	rpc->add(ctx, "d", my_pid());
+}
+#endif

+ 369 - 0
modules/binrpc/ctrl_socks.c

@@ -0,0 +1,369 @@
+/*
+ * $Id: ctrl_socks.c,v 1.2 2006/02/23 21:14:02 andrei Exp $
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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
+ */
+/* History:
+ * --------
+ *  2006-02-14  created by andrei
+ *  2007        ported to libbinrpc (bpintea)
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "ctrl_socks.h"
+#include "init_socks.h"
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../ut.h"
+
+#include "proctab.h"
+#ifdef USE_FIFO
+#include "fifo_server.h"
+#endif
+
+struct ctrl_socket* ctrl_sock_lst=0;
+
+int init_ctrl_socket(struct ctrl_socket* ctrl_sock, int perm, int uid, int gid)
+{
+	int s;
+#ifdef USE_FIFO
+	int extra_fd;
+#endif
+
+	switch (ctrl_sock->addr.domain) {
+		case PF_LOCAL:
+			s = init_unix_sock(&ctrl_sock->addr, perm, uid, gid);
+			break;
+		case PF_INET:
+		case PF_INET6:
+			s = init_tcpudp_sock(&ctrl_sock->addr);
+			break;
+#ifdef USE_FIFO
+		case PF_FIFO:
+			s=init_fifo_fd(ctrl_sock->name, perm, uid, gid, &extra_fd);
+			ctrl_sock->write_fd = extra_fd;
+			break;
+#endif
+		default:
+			BUG("unknown domain %d.\n", ctrl_sock->addr.domain);
+			return -1;
+	}
+	
+	if (s < 0) {
+		ERR("failed to setup socket for URI `%s'.\n", CTRLSOCK_URI(ctrl_sock));
+		return -1;
+	}
+	ctrl_sock->fd = s;
+	return 0;
+}
+
+
+int add_binrpc_listener(char *s)
+{
+	brpc_addr_t *addr;
+	struct ctrl_socket *listener;
+	
+	DEBUG("adding BINRPC URI `%s' to listeners list.\n", s);
+	addr = brpc_parse_uri(s);
+	if (! addr) {
+		ERR("invalid BINRPC URI `%s' (%s).\n", s, brpc_strerror());
+		return -1;
+	}
+	listener = (struct ctrl_socket *)pkg_malloc(sizeof(struct ctrl_socket));
+	if (! listener) {
+		ERR("out of pkg memory.\n");
+		return -1;
+	}
+	memset((char *)listener, 0, sizeof(struct ctrl_socket));
+	listener->addr = *addr;
+	listener->name = s + (sizeof(BRPC_URI_PREFIX) - /*0-term*/1);
+	listener->p_proto = P_BINRPC;
+	listener->next = ctrl_sock_lst;
+	ctrl_sock_lst = listener;
+	return 0;
+}
+
+struct ctrl_socket *add_fdpass_socket(int sockfd, pid_t pid)
+{
+	struct ctrl_socket *cs;
+	DEBUG("adding worker socket for child PID#%d.\n", pid);
+
+	if (! (cs = pkg_malloc(sizeof(struct ctrl_socket)))) {
+		ERR("out of pkg memory.\n");
+		return NULL;
+	}
+	memset(cs, 0, sizeof(struct ctrl_socket));
+	BRPC_ADDR_DOMAIN(&cs->addr) = PF_LOCAL;
+	BRPC_ADDR_TYPE(&cs->addr) = SOCK_STREAM;
+	cs->fd = sockfd;
+	cs->p_proto = P_FDPASS;
+	cs->child = pid;
+	cs->next = ctrl_sock_lst;
+	ctrl_sock_lst = cs;
+	return cs;
+}
+
+#ifdef USE_FIFO
+/*
+ *
+ * TODO: this could be complete baloney!!! 
+ * (should probably go to fifo_server*)
+ *
+ */
+static brpc_addr_t *get_fifo_addr(char *path)
+{
+	static brpc_addr_t addr;
+	size_t pathlen;
+
+	memset((char *)&addr, 0, sizeof(brpc_addr_t));
+	addr.domain = PF_FIFO;
+	pathlen = strlen(path) + /*0-term*/1;
+	if (sizeof(addr.sockaddr.un.sun_path) < pathlen) {
+		ERR("FIFO path `%s' too long (maximum supported: %zd).\n", path, 
+				sizeof(addr.sockaddr.un.sun_path));
+		return NULL;
+	}
+	memcpy(addr.sockaddr.un.sun_path, path, pathlen);
+	return &addr;
+}
+
+int add_fifo_socket(char *s)
+{
+	brpc_addr_t *addr;
+	struct ctrl_socket *listener;
+	
+	addr = get_fifo_addr(s);
+	if (! addr)
+		return -1;
+
+	listener = (struct ctrl_socket *)pkg_malloc(sizeof(struct ctrl_socket));
+	if (! listener) {
+		ERR("out of pkg memory.\n");
+		return -1;
+	}
+	memset(listener, 0, sizeof(struct ctrl_socket));
+	memcpy((char *)&listener->addr, addr, sizeof(brpc_addr_t));
+	listener->name = s;
+	listener->p_proto = P_FIFO;
+
+	listener->next = ctrl_sock_lst;
+	ctrl_sock_lst = listener;
+	return 0;
+}
+#endif /* USE_FIFO */
+
+
+
+static void close_binrpc_listener(struct ctrl_socket *cs)
+{
+	char *p_type, *p_addr;
+
+	if (cs->fd < 0)
+		/* already closed */
+		return;
+
+	switch (cs->p_proto) {
+		case P_BINRPC: 
+			p_type = "BINRPC"; 
+			p_addr = brpc_print_addr(&cs->addr);
+			break;
+#ifdef USE_FIFO
+		case P_FIFO:
+			/* TODO */
+			break;
+#endif
+		case P_FDPASS:
+			p_type = "FDPASS";
+			p_addr = "[sockpair]";
+			break;
+
+		default: 
+			BUG("unexpected protocol type: %d.\n", cs->p_proto);
+#ifdef EXTRA_DEBUG
+			abort();
+#endif
+			return;
+	}
+	DEBUG("closing %s listener: %s.\n", p_type, p_addr);
+
+	/* close all the opened fds & unlink the files */
+	switch(cs->addr.domain){
+		case PF_LOCAL:
+			close(cs->fd);
+			cs->fd=-1;
+#if USE_FIFO
+			/* TODO: does this make sense? "write_fd - only used by fifo"*/
+			if (cs->write_fd!=-1){
+				close(cs->write_fd);
+				cs->write_fd=-1;
+			}
+#endif
+			break;
+#ifdef USE_FIFO
+		case PF_FIFO:
+			destroy_fifo(cs->fd, cs->write_fd, cs->name);
+			break;
+#endif
+		default:
+			close(cs->fd);
+			cs->fd=-1;
+#if USE_FIFO
+			/* TODO: does this make sense? "write_fd - only used by fifo"*/
+			if (cs->write_fd!=-1){
+				close(cs->write_fd);
+				cs->write_fd=-1;
+			}
+#endif
+	}
+}
+
+static void del_binrpc_listener(struct ctrl_socket *cs)
+{
+	close_binrpc_listener(cs);
+	pkg_free(cs);
+}
+
+
+static inline enum SOCK_LIST_TYPES get_socklist_type(struct ctrl_socket *cs)
+{
+	if (cs->p_proto == P_FDPASS)
+		return SOCKLIST_FDPASS;
+	switch (BRPC_ADDR_TYPE(&cs->addr)) {
+		case SOCK_STREAM: return SOCKLIST_STREAM;
+		case SOCK_DGRAM: return SOCKLIST_DGRAM;
+		default:
+			BUG("invalid address type %d.\n", BRPC_ADDR_TYPE(&cs->addr));
+#ifdef EXTRA_DEBUG
+			abort();
+#endif
+			return SOCKLIST_NONE;
+	}
+}
+
+/**
+ * @param keep Enum SOCK_LIST_TYPES bit mask
+ */
+void del_binrpc_listeners(int keep)
+{
+	struct ctrl_socket *next, *cs, *prev;
+
+	cs = ctrl_sock_lst;
+	prev = NULL;
+	while (cs) {
+		if ((get_socklist_type(cs) & keep) == 0) {
+			/* TODO: FIFO */
+			next = cs->next;
+			del_binrpc_listener(cs);
+			cs = next;
+			
+			if (prev)
+				prev->next = cs;
+			else
+				ctrl_sock_lst = cs;
+		} else {
+			prev = cs;
+			cs = cs->next;
+		}
+	}
+}
+
+/**
+ * @param keep Enum SOCK_LIST_TYPES bit mask
+ */
+void close_binrpc_listeners(int keep)
+{
+	struct ctrl_socket *cs;
+
+	for (cs = ctrl_sock_lst; cs; cs = cs->next)
+		if ((get_socklist_type(cs) & keep) == 0)
+			close_binrpc_listener(cs);
+}
+
+char **ctrl_sock_paths()
+{
+	char **paths, **p;
+	size_t cnt, i;
+	struct ctrl_socket *cs;
+			
+	for (cs = ctrl_sock_lst, cnt = 0, paths = NULL; cs; cs = cs->next) {
+		if (BRPC_ADDR_DOMAIN(&cs->addr) != PF_LOCAL)
+			continue;
+		if (! (p = (char **)realloc(paths, (cnt + 1) * sizeof(char *)))) {
+			ERR("out of system memory!\n");
+			goto err;
+		}
+		paths = p;
+		if (! (paths[cnt] = strdup(cs->name))) {
+			ERR("out of system memory!\n");
+			goto err;
+		}
+		cnt ++;
+	}
+	/* add NULL as end marker */
+	if (! (p = realloc(paths, (cnt + 1) * sizeof(char *)))) {
+		ERR("out of system memory!\n");
+		goto err;
+	}
+	(paths = p)[cnt] = NULL;
+
+
+	return paths;
+err:
+	for (i = 0; i < cnt; i ++)
+		free(paths[i]);
+	free(paths);
+	return NULL;
+}
+
+char* payload_proto_name(enum payload_proto p)
+{
+	switch(p){
+		case P_BINRPC:
+			return "binrpc";
+#ifdef USE_FIFO
+		case P_FIFO:
+			return "fifo";
+#endif
+		default:
+			;
+	}
+	return "<unknown>";
+}
+
+
+const char* ctl_listen_ls_doc[]={ "list ctl listen sockets", 0 };
+
+void  ctrl_listen_ls_rpc(rpc_t* rpc, void* ctx)
+{
+	struct ctrl_socket* cs;
+	
+	for (cs=ctrl_sock_lst; cs; cs=cs->next){
+		if (cs->p_proto == P_FDPASS)
+			continue;
+		rpc->add(ctx, "ssss", payload_proto_name(cs->p_proto),
+						socket_proto_name(&cs->addr), cs->name, "");
+	}
+}

+ 105 - 0
modules/binrpc/ctrl_socks.h

@@ -0,0 +1,105 @@
+/*
+ * $Id: ctrl_socks.h,v 1.1 2006/02/23 19:57:31 andrei Exp $
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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
+ */
+/* History:
+ * --------
+ *  2006-02-14  created by andrei
+ *  2007        ported to libbinrpc (bpintea)
+ */
+
+#ifndef _ctrl_socks_h
+#define _ctrl_socks_h
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h> /* iovec */
+#include <unistd.h> /* pid_t */
+#include "../../ip_addr.h"
+#include "../../rpc.h"
+
+#include <binrpc.h>
+
+#include "init_socks.h"
+
+enum payload_proto {
+	P_BINRPC, /* transport BINRPC protocol bytes */
+#ifdef USE_FIFO
+	P_FIFO, /* transport FIFO protocol bytes */
+#endif
+	P_FDPASS /* transport descriptors */
+};
+
+enum SOCK_LIST_TYPES {
+	SOCKLIST_NONE	= 0,
+	SOCKLIST_STREAM	= 1 << 0,
+	SOCKLIST_DGRAM	= 1 << 1,
+#ifdef USE_FIFO
+	SOCKLIST_FIFO	= 1 << 2,
+#endif
+	SOCKLIST_FDPASS	= 1 << 3,
+};
+
+
+/* list of control sockets */
+struct ctrl_socket {
+	int fd;
+#ifdef USE_FIFO
+	int write_fd; /* used only by fifo */
+#endif
+	enum payload_proto p_proto;
+	brpc_addr_t addr; /* addresses where the "control" sockets listen */
+	union {
+		char* name;
+		pid_t child; /* == 0 for the children */
+	};
+	struct ctrl_socket* next;
+	void *data; /* extra data, socket dependent */
+};
+
+extern struct ctrl_socket* ctrl_sock_lst;
+
+int init_ctrl_socket(struct ctrl_socket* ctrl_sock, int perm, 
+		int uid, int gid);
+int add_binrpc_listener(char *s);
+struct ctrl_socket *add_fdpass_socket(int sockfd, pid_t pid);
+#ifdef USE_FIFO
+int add_fifo_socket(char *s);
+#endif
+void del_binrpc_listeners(int keep);
+void close_binrpc_listeners(int keep);
+char **ctrl_sock_paths();
+char* payload_proto_name(enum payload_proto p);
+
+#define CTRLSOCK_URI(_cs_) \
+	((_cs_)->p_proto == P_FDPASS) ? \
+			"(FDPASS)" :  \
+			brpc_print_addr(&(_cs_)->addr)
+
+extern const char *ctl_listen_ls_doc[];
+void  ctrl_listen_ls_rpc(rpc_t* rpc, void* ctx);
+
+
+#endif

+ 1615 - 0
modules/binrpc/fifo_server.c

@@ -0,0 +1,1615 @@
+/*
+ * $Id: fifo_server.c,v 1.2 2006/03/13 10:24:54 janakj Exp $
+ *
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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
+ *
+ * Fifo server is a very powerful tool used to access easily
+ * ser's internals via textual interface, similarly to
+ * how internals of many operating systems are accessible
+ * via the proc file system. This might be used for
+ * making ser do things for you (such as initiating new
+ * transaction from webpages) or inspect server's health.
+ * 
+ * FIFO server allows new functionality to be registered
+ * with it -- thats what register_fifo_cmd is good for.
+ * Remember, the initialization must take place before
+ * forking; best in init_module functions. When a function
+ * is registered, it can be always evoked by sending its
+ * name prefixed by colon to the FIFO.
+ *
+ * There are few commands already implemented in core.
+ * These are 'uptime' for looking at how long the server
+ * is alive and 'print' for debugging purposes.
+ *
+ * Every command sent to FIFO must be sent atomically to
+ * avoid intermixing with other commands and MUST be
+ * terminated by empty line so that the server is to able
+ * to find its end if it does not understand the command.
+ *
+ * File test/transaction.fifo illustrates example of use
+ * of t_uac command (part of TM module).
+ *
+ * History:
+ * --------
+ *  2003-03-29  destroy pkg mem introduced (jiri)
+ *  2003-03-19  replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
+ *  2003-01-29  new built-in fifo commands: arg and pwd (jiri)
+ *  2003-10-07  fifo security fixes: permissions, always delete old fifo,
+ *               reply fifo checks -- added fifo_check (andrei)
+ *  2003-10-13  added fifo_dir for reply fifos (andrei)
+ *  2003-10-30  DB interface exported via FIFO (bogdan)
+ *  2004-03-09  open_fifo_server split into init_ and start_ (andrei)
+ *  2004-04-29  added chown(sock_user, sock_group)  (andrei)
+ *  2004-06-06  updated to the new DB interface  & init_db_fifo (andrei)
+ *  2004-09-19  fifo is deleted on exit (destroy_fifo)  (andrei)
+ *  2005-03-02  meminfo fifo cmd added (andrei)
+ *  2006-02-17  hacked to process fifo request as a part of the ctrl module
+ *              and to work also over tcp, udp or unix sockets (andrei)
+ */
+
+#ifdef USE_FIFO
+
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#ifdef USE_TCP
+#include <sys/socket.h>
+#endif
+
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../error.h"
+#include "../../config.h"
+#include "../../globals.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../sr_module.h"
+#include "../../pt.h"
+#include "../../rpc.h"
+#include "../../tsend.h"
+#include "fifo_server.h"
+#include "io_listener.h"
+
+
+#define MAX_FIFO_COMMAND        128    /* Maximum length of a FIFO server command */
+#define MAX_CONSUME_BUFFER     1024    /* Buffer dimensions for FIFO server */
+#define MAX_LINE_BUFFER        2048    /* Maximum parameter line length */
+#define DEFAULT_REPLY_RETRIES     4    /* Default number of reply write attempts */
+#define DEFAULT_REPLY_WAIT    80000    /* How long we should wait for the client, in micro seconds */
+#define DEFAULT_FIFO_DIR    "/tmp/"    /* Where reply pipes may be opened */
+#define MAX_MSG_CHUNKS        1024     /* maximum message pieces */
+#define FIFO_TX_TIMEOUT        200     /* maximum time to block writing to
+                                          the fifo */
+
+/* readline from a buffer helper */
+struct readline_handle{ 
+	char* s;    /* buffer start */
+	char* end;  /* end */
+	char* crt;  /* crt. pos */
+};
+
+enum text_flags {
+	CHUNK_SEEN         = (1 << 0),
+	CHUNK_POSITIONAL   = (1 << 1), /* Positinal parameter, should be followed by \n */
+	CHUNK_MEMBER_NAME  = (1 << 2), /* Struct member name, should be followed by : */
+	CHUNK_MEMBER_VALUE = (1 << 3)  /* Struct member value, should be followed by , if
+					* there is another member name and \n if not */
+};
+	
+
+/*
+ * Generit text chunk. Flags attribute contains arbitrary flags
+ */
+struct text_chunk {
+	unsigned char flags;
+	str s;
+	struct text_chunk* next;
+	void *ctx; /* context, which must be passed along */
+};
+
+
+/* 
+ * This is the parameter if rpc_struct_add 
+ */
+struct rpc_struct_out {
+	struct rpc_context* ctx;
+	struct text_chunk* line;
+};
+
+
+struct rpc_struct {
+	struct rpc_context* ctx;
+	struct text_chunk* names;  /* Names of elements */
+	struct text_chunk* values; /* Element values as strings */
+	struct rpc_struct* next;
+};
+
+
+/*
+ * Context structure containing state of processing
+ */
+typedef struct rpc_context {
+	char* method;               /* Request method name */
+	char* reply_file;           /* Full path and name to the reply FIFO file */
+	int reply_sent;             /* This flag ensures that we do not send a reply twice */
+	int code;                   /* Reply code */
+	char* reason;               /* Reason phrase */
+	struct text_chunk* body;    /* First line to be appended as reply body */
+	struct text_chunk* last;    /* Last body line */
+	struct text_chunk* strs;    /* Strings to be collected at the end of processing */
+	struct rpc_struct* structs; /* Structures to be collected at the end of processing */
+	struct readline_handle read_h;
+	struct send_handle* send_h;
+	int line_no;
+} rpc_ctx_t;
+
+
+
+
+char* fifo_dir           = DEFAULT_FIFO_DIR;       /* dir where reply fifos are
+													  allowed */
+int   fifo_reply_retries = DEFAULT_REPLY_RETRIES;
+int   fifo_reply_wait    = DEFAULT_REPLY_WAIT;
+
+
+static rpc_t     func_param;        /* Pointers to implementation of RPC funtions */
+
+static int  rpc_send         (rpc_ctx_t* ctx);                                 /* Send the reply to the client */
+static void rpc_fault        (rpc_ctx_t* ctx,       int code, char* fmt, ...); /* Signal a failure to the client */
+static int  rpc_add          (rpc_ctx_t* ctx,       char* fmt, ...);           /* Add a new piece of data to the result */
+static int  rpc_scan         (rpc_ctx_t* ctx,       char* fmt, ...);           /* Retrieve request parameters */
+static int  rpc_printf       (rpc_ctx_t* ctx,       char* fmt, ...);           /* Add printf-like formated data to the result set */
+static int  rpc_struct_add   (struct text_chunk* s, char* fmt, ...);           /* Create a new structure */
+static int  rpc_struct_scan  (struct rpc_struct* s, char* fmt, ...);           /* Scan attributes of a structure */
+static int  rpc_struct_printf(struct text_chunk* s, char* name, char* fmt, ...);
+
+
+/*
+ * Escape string in buffer 'r' of length len. Write
+ * the escaped string in buffer dst. The destination
+ * buffer must exist and must be twice as big as the
+ * input buffer.
+ *
+ * Parameter all controls the set of characters to be
+ * escaped. If set to 1 then all characters, including
+ * structure delimiters, will be escaped. If set to
+ * 0 then only line delimiters, tab and zero will be
+ * escaped.
+ */
+static void escape(str* dst, char* r, int len, int all)
+{
+	int i;
+	char* w;
+	if (!len) { 
+		dst->len = 0;
+		return;
+	}
+
+	w = dst->s;
+	for(i = 0; i < len; i++) {
+		switch(r[i]) {
+		case '\n': *w++ = '\\'; *w++ = 'n';  break;
+		case '\r': *w++ = '\\'; *w++ = 'r';  break;
+		case '\t': *w++ = '\\'; *w++ = 't';  break;
+		case '\\': *w++ = '\\'; *w++ = '\\'; break;
+		case '\0': *w++ = '\\'; *w++ = '0';  break;
+		case ':': 
+			if (all) {
+				*w++ = '\\';
+				*w++ = 'o';
+			} else *w++ = r[i];
+			break;
+			
+		case ',':
+			if (all) {
+				*w++ = '\\';
+				*w++ = 'c';
+			} else *w++ = r[i];
+			break;
+
+		default:
+			*w++ = r[i];
+			break;
+		}
+	}
+	dst->len = w - dst->s;
+}
+
+
+/*
+ * Unescape the string in buffer 'r' of length len.
+ * The resulting string will be stored in buffer dst
+ * which must exist and must be at least as big as
+ * the source buffer. The function will update dst->len
+ * to the length of the resulting string.
+ *
+ * Return value 0 indicates success, -1 indicates
+ * formatting error.
+ */
+static int unescape(str* dst, char* r, int len)
+{
+	char* w;
+	int i;
+
+	if (!len) {
+		dst->len = 0;
+		return 0;
+	}
+
+	w = dst->s;
+	for(i = 0; i < len; i++) {
+		switch(*r) {
+		case '\\':
+			r++;
+			i++;
+			switch(*r++) {
+			case '\\': *w++ = '\\'; break;
+			case 'n':  *w++ = '\n'; break;
+			case 'r':  *w++ = '\r'; break;
+			case 't':  *w++ = '\t'; break;
+			case '0':  *w++ = '\0'; break;
+			case 'c':  *w++ = ':';  break; /* Structure delimiter */
+			case 'o':  *w++ = ',';  break; /* Structure delimiter */
+			default:   return -1;
+			}
+			break;
+
+		default: *w++ = *r++; break;
+		}
+	}
+	dst->len = w - dst->s;
+	return 0;
+}
+
+
+/*
+ * Create a new text chunk, the input text will
+ * be escaped.
+ */
+struct text_chunk* new_chunk_escape(str* src, int escape_all)
+{
+	struct text_chunk* l;
+	if (!src) return 0;
+
+        l = pkg_malloc(sizeof(struct text_chunk));
+	if (!l) {
+		ERR("No Memory Left\n");
+		return 0;
+	}
+	l->s.s = pkg_malloc(src->len * 2 + 1);
+	if (!l->s.s) {
+		ERR("No Memory Left\n");
+		pkg_free(l);
+		return 0;
+	}
+	l->next = 0;
+	l->flags = 0;
+	escape(&l->s, src->s, src->len, escape_all);
+	l->s.s[l->s.len] = '\0';
+	return l;
+}
+
+/*
+ * Create a new text chunk, the input text
+ * will not be escaped. The function returns
+ * 0 on an error
+ */
+struct text_chunk* new_chunk(str* src)
+{
+	struct text_chunk* l;
+	if (!src) return 0;
+
+        l = pkg_malloc(sizeof(struct text_chunk));
+	if (!l) {
+		ERR("No Memory Left\n");
+		return 0;
+	}
+	l->s.s = pkg_malloc(src->len + 1);
+	if (!l->s.s) {
+		ERR("No Memory Left\n");
+		pkg_free(l);
+		return 0;
+	}
+	l->next = 0;
+	l->flags = 0;
+	memcpy(l->s.s, src->s, src->len);
+	l->s.len = src->len;
+	l->s.s[l->s.len] = '\0';
+	return l;
+}
+
+
+/*
+ * Create a new text chunk, the input text
+ * will be unescaped first.
+ */
+struct text_chunk* new_chunk_unescape(str* src)
+{
+	struct text_chunk* l;
+	if (!src) return 0;
+
+        l = pkg_malloc(sizeof(struct text_chunk));
+	if (!l) {
+		ERR("No Memory Left\n");
+		return 0;
+	}
+	l->s.s = pkg_malloc(src->len + 1);
+	if (!l->s.s) {
+		ERR("No Memory Left\n");
+		pkg_free(l);
+		return 0;
+	}
+	l->next = 0;
+	l->flags = 0;
+	if (unescape(&l->s, src->s, src->len) < 0) {
+		pkg_free(l->s.s);
+		pkg_free(l);
+		return 0;
+	}
+	l->s.s[l->s.len] = '\0';
+	return l;
+}
+
+
+static void free_chunk(struct text_chunk* c)
+{
+	if (c && c->s.s) pkg_free(c->s.s);
+	if (c) pkg_free(c);
+}
+
+
+static void free_struct(struct rpc_struct* s)
+{
+	struct text_chunk* c;
+
+	if (!s) return;
+	while(s->names) {
+		c = s->names;
+		s->names = s->names->next;
+		free_chunk(c);
+	}
+
+	while(s->values) {
+		c = s->values;
+		s->values = s->values->next;
+		free_chunk(c);
+	}
+
+	pkg_free(s);
+}
+
+
+/*
+ * Parse a structure
+ */
+static struct rpc_struct* new_struct(rpc_ctx_t* ctx, str* line)
+{
+	char* comma, *colon;
+	struct rpc_struct* s;
+	str left, right = STR_NULL, name, value;
+	struct text_chunk* n, *v;
+
+	if (!line->len) {
+		rpc_fault(ctx, 400, "Line %d Empty - Structure Expected", 
+					ctx->line_no);
+		return 0;
+	}
+
+	s = (struct rpc_struct*)pkg_malloc(sizeof(struct rpc_struct));
+	if (!s) {
+		rpc_fault(ctx, 500, "Internal Server Error (No Memory Left)");
+		return 0;
+	}
+	memset(s, 0, sizeof(struct rpc_struct));
+	s->ctx = ctx;
+	
+	left = *line;
+	do {
+		comma = q_memchr(left.s, ',', left.len);
+		if (comma) {
+			right.s = comma + 1;
+			right.len = left.len - (comma - left.s) - 1;
+			left.len = comma - left.s;
+		}
+		
+		     /* Split the record to name and value */
+		colon = q_memchr(left.s, ':', left.len);
+		if (!colon) {
+			rpc_fault(ctx, 400, "Colon missing in struct on line %d",
+							ctx->line_no);
+			goto err;;
+		}
+		name.s = left.s;
+		name.len = colon - name.s;
+		value.s = colon + 1;
+		value.len = left.len - (colon - left.s) - 1;
+		
+		     /* Create name chunk */
+		n = new_chunk_unescape(&name);
+		if (!n) {
+			rpc_fault(ctx, 400, "Error while processing struct member '%.*s' "
+						"on line %d", name.len, ZSW(name.s), ctx->line_no);
+			goto err;
+		}
+		n->next = s->names;
+		s->names = n;
+
+		     /* Create value chunk */
+		v = new_chunk_unescape(&value);
+		if (!v) {
+			rpc_fault(ctx, 400, "Error while processing struct membeer '%.*s'"
+						" on line %d", name.len, ZSW(name.s), ctx->line_no);
+			goto err;
+		}
+		v->next = s->values;
+		s->values = v;
+
+		left = right;
+	} while(comma);
+
+	return s;
+ err:
+	if (s) free_struct(s);
+	return 0;
+}
+
+
+/*
+ * Read a line from FIFO file and store a pointer to the data in buffer 'b'
+ * and the legnth of the line in variable 'read'
+ *
+ * Returns -1 on error, 0 on success
+ */
+static int read_line(char** b, int* read, struct readline_handle* rh)
+{
+	char* eol;
+	char* trim;
+	
+	if (rh->crt>=rh->end){
+		/* end, nothing more to read */
+		return -1;
+	}
+	for(eol=rh->crt; (eol<rh->end) && (*eol!='\n'); eol++);
+	*eol=0;
+	trim=eol;
+	/* trim spaces at the end */
+	for(trim=eol;(trim>rh->crt) && 
+			((*trim=='\r')||(*trim==' ')||(*trim=='\t')); trim--){
+		*trim=0;
+	}
+	*b=rh->crt;
+	*read = (int)(trim-rh->crt);
+	rh->crt=eol+1;
+	return 0;
+}
+
+
+/*
+ * Remove directory path from filename and replace it
+ * with the path configured through a module parameter.
+ * 
+ * The result is allocated using pkg_malloc and thus
+ * has to be freed using pkg_free
+ */
+static char *trim_filename(char * file)
+{
+	int prefix_len, fn_len;
+	char *new_fn;
+	
+	/* we only allow files in "/tmp" -- any directory
+	 * changes are not welcome
+	 */
+	if (strchr(file, '.') || strchr(file, '/')
+	    || strchr(file, '\\')) {
+		ERR("Forbidden filename: %s\n"
+		    , file);
+		return 0;
+	}
+	prefix_len = strlen(fifo_dir); fn_len = strlen(file);
+	new_fn = pkg_malloc(prefix_len + fn_len + 1);
+	if (new_fn == 0) {
+		ERR("No memory left\n");
+		return 0;
+	}
+
+	memcpy(new_fn, fifo_dir, prefix_len);
+	memcpy(new_fn + prefix_len, file, fn_len);
+	new_fn[prefix_len + fn_len] = 0;
+	return new_fn;
+}
+
+
+
+/* reply fifo security checks:
+ * checks if fd is a fifo, is not hardlinked and it's not a softlink
+ * opened file descriptor + file name (for soft link check)
+ * returns 0 if ok, <0 if not 
+ */
+static int fifo_check(int fd, char* fname)
+{
+	struct stat fst;
+	struct stat lst;
+	
+	if (fstat(fd, &fst) < 0) {
+		ERR("fstat failed: %s\n",
+		    strerror(errno));
+		return -1;
+	}
+	     /* check if fifo */
+	if (!S_ISFIFO(fst.st_mode)){
+		ERR("%s is not a fifo\n", fname);
+		return -1;
+	}
+	     /* check if hard-linked */
+	if (fst.st_nlink > 1) {
+		ERR("%s is hard-linked %d times\n",
+		    fname, (unsigned)fst.st_nlink);
+		return -1;
+	}
+	
+	     /* lstat to check for soft links */
+	if (lstat(fname, &lst) < 0) {
+		ERR("lstat failed: %s\n",
+		    strerror(errno));
+		return -1;
+	}
+	if (S_ISLNK(lst.st_mode)) {
+		ERR("%s is a soft link\n", fname);
+		return -1;
+	}
+	     /* if this is not a symbolic link, check to see if the inode didn't
+	      * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
+	      */
+	if ((lst.st_dev != fst.st_dev) || (lst.st_ino != fst.st_ino)) {
+		ERR("inode/dev number differ : %d %d (%s)\n",
+		    (int)fst.st_ino, (int)lst.st_ino, fname);
+		return -1;
+	}
+	     /* success */
+	return 0;
+}
+
+
+/*
+ * Open the FIFO reply file
+ * returns fs no on success, -1 on error
+ */
+static int open_reply_pipe(char *pipe_name)
+{
+	
+	int fifofd;
+	int flags;
+	
+	int retries = fifo_reply_retries;
+	
+	fifofd=-1;
+	if (!pipe_name || *pipe_name == 0) {
+		DBG("No file to write to about missing cmd\n");
+		goto error;
+	}
+	
+ tryagain:
+	     /* open non-blocking to make sure that a broken client will not 
+	      * block the FIFO server forever */
+	fifofd = open(pipe_name, O_WRONLY | O_NONBLOCK);
+	if (fifofd == -1) {
+		     /* retry several times if client is not yet ready for getting
+		      * feedback via a reply pipe
+		      */
+		if (errno == ENXIO) {
+			     /* give up on the client - we can't afford server blocking */
+			if (retries == 0) {
+				ERR("No client at %s\n", pipe_name);
+				goto error;
+			}
+			     /* don't be noisy on the very first try */
+			if (retries != fifo_reply_retries) {
+				DBG("Retry countdown: %d\n", retries);
+			}
+			sleep_us(fifo_reply_wait);
+			retries--;
+			goto tryagain;
+		}
+		     /* some other opening error */
+		ERR("Open error (%s): %s\n",
+		    pipe_name, strerror(errno));
+		goto error;
+	}
+	     /* security checks: is this really a fifo?, is 
+	      * it hardlinked? is it a soft link? */
+	if (fifo_check(fifofd, pipe_name) < 0) goto error;
+	
+	     /* we want server blocking for big writes */
+	if ((flags = fcntl(fifofd, F_GETFL, 0)) < 0) {
+		ERR("(%s): getfl failed: %s\n", pipe_name, strerror(errno));
+		goto error;
+	}
+	flags &= ~O_NONBLOCK;
+	if (fcntl(fifofd, F_SETFL, flags) < 0) {
+		ERR("(%s): setfl cntl failed: %s\n",
+		    pipe_name, strerror(errno));
+		goto error;
+	}
+	
+	return fifofd;
+ error:
+	if (fifofd!=-1) close(fifofd);
+	return -1;
+}
+
+
+
+
+/*
+ * Man FIFO routine running in the FIFO
+ * processes requests received
+ * through the FIFO file repeatedly
+ */
+int fifo_process(char* msg_buf, int size, int* bytes_needed, void *sh,
+					void** saved_state)
+{
+	rpc_export_t* exp;
+	char* buf;
+	int line_len;
+	char *file_sep;
+	struct text_chunk* p;
+	struct rpc_struct* s;
+	int r;
+	int req_size;
+	static rpc_ctx_t context; 
+
+	DBG("process_fifo: called with %d bytes, offset %d: %.*s\n",
+			size, (int)(long)*saved_state, size, msg_buf);
+	/* search for the end of the request (\n\r) */
+	if (size < 6){ /* min fifo request */
+		*bytes_needed=6-size;
+		return 0; /* we want more bytes, nothing processed */
+	}
+	for (r=1+(int)(long)*saved_state;r<size;r++){
+		if ((msg_buf[r]=='\n' || msg_buf[r]=='\r') &&
+			(msg_buf[r-1]=='\n'|| msg_buf[r-1]=='\r')){
+			/* found double cr, or double lf => end of request */
+			req_size=r;
+			goto process;
+		}
+	}
+	/* no end of request found => ask for more bytes */
+	*bytes_needed=1;
+	/* save current offset, to optimize search */
+	*saved_state=(void*)(long)(r-1);
+	return 0; /* we want again the whole buffer */
+process:
+	
+	DBG("process_fifo  %d bytes request: %.*s\n", 
+			req_size, req_size, msg_buf);
+	file_sep = 0;
+	context.method = 0;
+	context.reply_file = 0;
+	context.body = 0;
+	context.code = 200;
+	context.reason = "OK";
+	context.reply_sent = 0;
+	context.last = 0;
+	context.line_no = 0;
+	context.read_h.s=msg_buf;
+	context.read_h.end=msg_buf+size;
+	context.read_h.crt=msg_buf;
+	context.send_h=(struct send_handle*)sh;
+		     /* commands must look this way ':<command>:[filename]' */
+		if (read_line(&buf, &line_len, &context.read_h) < 0) {
+			     /* line breaking must have failed -- consume the rest
+			      * and proceed to a new request
+			      */
+			ERR("Command expected\n");
+			goto consume;
+		}
+		context.line_no++;
+		if (line_len == 0) {
+			DBG("Empty command received\n");
+			goto consume;
+		}
+		if (line_len < 3) {
+			ERR("Command must have at least 3 chars\n");
+			goto consume;
+		}
+		if (*buf != CMD_SEPARATOR) {
+			ERR("Command must begin with %c: %.*s\n", 
+			    CMD_SEPARATOR, line_len, buf);
+			goto consume;
+		}
+
+		context.method = buf + 1;
+		file_sep = strchr(context.method, CMD_SEPARATOR);
+		if (file_sep == NULL) {
+			ERR("File separator missing\n");
+			goto consume;
+		}
+		if ((file_sep == context.method)) {
+			ERR("Empty command\n");
+			goto consume;
+		}
+		if (*(file_sep + 1) == 0) context.reply_file = NULL; 
+		else {
+			context.reply_file = file_sep + 1;
+			context.reply_file = trim_filename(context.reply_file);
+			if (context.reply_file == 0) {
+				ERR("Trimming filename\n");
+				goto consume;
+			}
+		}
+		     /* make command zero-terminated */
+		*file_sep = 0;
+		
+		exp = find_rpc_export(context.method, 0);
+		if (!exp || !exp->function) {
+			DBG("Command %s not found\n", context.method);
+			rpc_fault(&context, 500, "Command '%s' not found", context.method);
+			goto consume;
+		}
+
+		exp->function(&func_param, &context);
+
+	consume:
+		if (!context.reply_sent) {
+			rpc_send(&context);
+		}
+
+		if (context.reply_file) { 
+			pkg_free(context.reply_file); 
+			context.reply_file = 0; 
+		}
+		
+		     /* Collect garbage (unescaped strings and structures) */
+		while(context.strs) {
+			p = context.strs;
+			context.strs = context.strs->next;
+			free_chunk(p);
+		}
+
+		while(context.structs) {
+			s = context.structs;
+			context.structs = context.structs->next;
+			free_struct(s);
+		}
+
+		*bytes_needed=0;
+		DBG("Command consumed\n");
+		DBG("process_fifo: returning %d, bytes_needed 0\n", req_size+1);
+		return req_size+1; /* all was processed (including terminating \n)*/
+
+}
+
+
+/*
+ * Initialze the FIFO fd
+ * Make sure that we can create and open the FIFO file and
+ * make it secure. This function must be executed from mod_init.
+ * This ensures that it has sufficient privileges.
+ */
+int init_fifo_fd(char* fifo, int fifo_mode, int fifo_uid, int fifo_gid,
+					int* fifo_write)
+{
+	struct stat filestat;
+	int n;
+	long opt;
+	int fifo_read;
+	
+	if (fifo == NULL) {
+		ERR("null fifo: no fifo will be opened\n");
+		/* error null fifo */
+		return -1;
+	}
+	if (strlen(fifo) == 0) {
+		ERR("emtpy fifo: fifo disabled\n");
+		return -1;
+	}
+	
+	
+	DBG("Opening fifo...\n");
+	n = stat(fifo, &filestat);
+	if (n == 0) {
+		/* FIFO exist, delete it (safer) */
+		if (unlink(fifo) < 0) {
+			ERR("Cannot delete old fifo (%s):"
+			    " %s\n", fifo, strerror(errno));
+			return -1;
+		}
+	} else if (n < 0 && errno != ENOENT) {
+		ERR("FIFO stat failed: %s\n",
+		    strerror(errno));
+	}
+	/* create FIFO ... */
+	if ((mkfifo(fifo, fifo_mode) < 0)) {
+		ERR("Can't create FIFO: "
+		    "%s (mode=%d)\n",
+		    strerror(errno), fifo_mode);
+		return -1;
+	} 
+	DBG("FIFO created @ %s\n", fifo );
+	if ((chmod(fifo, fifo_mode) < 0)) {
+		ERR("Can't chmod FIFO: %s (mode=%d)\n",
+		    strerror(errno), fifo_mode);
+		return -1;
+	}
+	if ((fifo_uid != -1) || (fifo_gid != -1)) {
+		if (chown(fifo, fifo_uid, fifo_gid) < 0) {
+			ERR("Failed to change the owner/group for %s  to %d.%d; %s[%d]\n",
+			    fifo, fifo_uid, fifo_gid, strerror(errno), errno);
+			return -1;
+		}
+	}
+	
+	DBG("fifo %s opened, mode=%d\n", fifo, fifo_mode);
+	
+	fifo_read = open(fifo, O_RDONLY | O_NONBLOCK, 0);
+	if (fifo_read < 0) {
+		ERR("fifo_read did not open: %s\n",
+		    strerror(errno));
+		return -1;
+	}
+	/* make sure the read fifo will not close */
+	*fifo_write = open(fifo, O_WRONLY | O_NONBLOCK, 0);
+	if (*fifo_write < 0) {
+		ERR("fifo_write did not open: %s\n",
+		    strerror(errno));
+		return -1;
+	}
+	/* set read fifo blocking mode */
+	if ((opt = fcntl(fifo_read, F_GETFL)) == -1) {
+		ERR("fcntl(F_GETFL) failed: %s [%d]\n",
+		    strerror(errno), errno);
+		return -1;
+	}
+	if (fcntl(fifo_read, F_SETFL, opt & (~O_NONBLOCK)) == -1) {
+		ERR("fcntl(F_SETFL) failed: %s [%d]\n",
+		    strerror(errno), errno);
+		return -1;
+	}
+	return fifo_read;
+}
+
+
+
+int fifo_rpc_init()
+{
+	func_param.send = (rpc_send_f)rpc_send;
+	func_param.fault = (rpc_fault_f)rpc_fault;
+	func_param.add = (rpc_add_f)rpc_add;
+	func_param.scan = (rpc_scan_f)rpc_scan;
+	func_param.printf = (rpc_printf_f)rpc_printf;
+	func_param.struct_add = (rpc_struct_add_f)rpc_struct_add;
+	func_param.struct_scan = (rpc_struct_scan_f)rpc_struct_scan;	
+	func_param.struct_printf = (rpc_struct_printf_f)rpc_struct_printf;
+	return 0;
+}
+
+
+
+/*
+ * Close and unlink the FIFO file
+ */
+void destroy_fifo(int read_fd, int w_fd, char* fname)
+{
+	if (read_fd!=-1)
+		close(read_fd);
+	if(w_fd!=-1)
+		close(w_fd);
+	/* if  FIFO was created, delete it */
+	if (fname && strlen(fname)) {
+		if (unlink(fname) < 0) {
+			WARN("Cannot delete fifo (%s):"
+			     " %s\n", fname, strerror(errno));
+		}
+	}
+}
+
+
+
+#define REASON_BUF_LEN 1024
+
+
+/*
+ * An error occurred, signal it to the client
+ */
+static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...)
+{
+	static char buf[REASON_BUF_LEN];
+	va_list ap;
+	ctx->code = code;
+	va_start(ap, fmt);
+	vsnprintf(buf, REASON_BUF_LEN, fmt, ap);
+	va_end(ap);
+	ctx->reason = buf;
+}
+
+
+static inline int safe_write(FILE* f, char* fmt, ...)
+{
+	va_list ap;
+	
+	if (!*fmt) return 0;
+	va_start(ap, fmt);
+
+ retry:
+	     /* First line containing code and reason phrase */
+	if (vfprintf(f, fmt, ap) <= 0) {
+		ERR("fifo write error: %s\n", strerror(errno));
+		if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+			goto retry;
+		}
+		va_end(ap);
+		return -1;
+	}
+	va_end(ap);
+	return 0;
+}
+
+
+
+inline static int build_iovec(rpc_ctx_t* ctx, struct iovec* v, int v_size) 
+{
+	struct text_chunk* p;
+	int r_c_len;
+	int r;
+	
+	
+	
+	/* reason code */
+	v[0].iov_base=int2str(ctx->code, &r_c_len);
+	v[0].iov_len=r_c_len;
+	v[1].iov_base=" ";
+	v[1].iov_len=1;
+	/* reason txt */
+	v[2].iov_base=ctx->reason;
+	v[2].iov_len=strlen(ctx->reason);
+	v[3].iov_base="\n";
+	v[3].iov_len=1;
+	r=4;
+	/* Send the body */
+	while(ctx->body) {
+		p = ctx->body;
+		ctx->body = ctx->body->next;
+		if (p->s.len){
+			if (r>=v_size) goto error_overflow;
+			v[r].iov_base=p->s.s;
+			v[r].iov_len=p->s.len;
+			r++;
+		}
+		if (p->flags & CHUNK_POSITIONAL) {
+			if (r>=v_size) goto error_overflow;
+			v[r].iov_base="\n";
+			v[r].iov_len=1;
+			r++;
+		} else if (p->flags & CHUNK_MEMBER_NAME) {
+			if (r>=v_size) goto error_overflow;
+			v[r].iov_base=":";
+			v[r].iov_len=1;
+			r++;
+		} else if (p->flags & CHUNK_MEMBER_VALUE) {
+			if (p->next && p->next->flags & CHUNK_MEMBER_NAME) {
+				if (r>=MAX_MSG_CHUNKS) goto error_overflow;
+				v[r].iov_base=",";
+				v[r].iov_len=1;
+				r++;
+			} else {
+				if (r>=v_size) goto error_overflow;
+				v[r].iov_base="\n";
+				v[r].iov_len=1;
+				r++;
+			}
+		}
+		free_chunk(p);
+	}
+	return r;
+error_overflow:
+	ERR("too many message chunks, iovec buffer overflow: %d/%d\n", r,
+			MAX_MSG_CHUNKS);
+	return -1;
+}
+
+
+
+/*
+ * Send a reply, either positive or negative, to the client
+ */
+static int rpc_send(rpc_ctx_t* ctx) 
+{
+	struct iovec v[MAX_MSG_CHUNKS];
+	int f;
+	int n;
+	int ret;
+	/* Send the reply only once */
+	if (ctx->reply_sent) return 1;
+	else ctx->reply_sent = 1;
+	
+	if ((n=build_iovec(ctx, v, MAX_MSG_CHUNKS))<0)
+		goto error;
+	if (ctx->send_h->type==S_FIFO){
+	/* Open the reply file */
+		f = open_reply_pipe(ctx->reply_file);
+		if (f == -1) {
+			ERR("No reply pipe %s\n", ctx->reply_file);
+			return -1;
+		}
+		ret=tsend_dgram_ev(f, v, n, FIFO_TX_TIMEOUT);
+		close(f);
+	}else{
+		ret=sock_send_v(ctx->send_h, v, n);
+	}
+	return (ret>=0)?0:-1;
+error:
+	ERR("rpc_send fifo error\n");
+	return -1;
+}
+
+
+
+/*
+ * Add a chunk to reply
+ */
+static void append_chunk(rpc_ctx_t* ctx, struct text_chunk* l)
+{
+	if (!ctx->last) {
+		ctx->body = l;
+		ctx->last = l;
+	} else {
+		ctx->last->next = l;
+		ctx->last = l;
+	}
+}
+
+
+/*
+ * Convert a value to data chunk and add it to reply
+ */
+static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap)
+{
+	struct text_chunk* l;
+	str str_val;
+	str* sp;
+	char buf[256];
+
+	switch(fmt) {
+	case 'd':
+	case 't':
+		str_val.s = int2str(va_arg(*ap, int), &str_val.len);
+		l = new_chunk(&str_val);
+		if (!l) {
+			rpc_fault(ctx, 500, "Internal server error while processing"
+					" line %d", ctx->line_no);
+			goto err;
+		}
+		break;
+		
+	case 'f':
+		str_val.s = buf;
+		str_val.len = snprintf(buf, 256, "%f", va_arg(*ap, double));
+		if (str_val.len < 0) {
+			rpc_fault(ctx, 400, "Error While Converting double");
+			ERR("Error while converting double\n");
+			goto err;
+		}
+		l = new_chunk(&str_val);
+		if (!l) {
+			rpc_fault(ctx, 500, "Internal Server Error, line %d",
+						ctx->line_no);
+			goto err;
+		}
+		break;
+		
+	case 'b':
+		str_val.len = 1;
+		str_val.s = ((va_arg(*ap, int) == 0) ? "0" : "1");
+		l = new_chunk(&str_val);
+		if (!l) {
+			rpc_fault(ctx, 500, "Internal Server Error, line %d", 
+						ctx->line_no);
+			goto err;
+		}
+		break;
+				
+	case 's':
+		str_val.s = va_arg(*ap, char*);
+		str_val.len = strlen(str_val.s);
+		l = new_chunk_escape(&str_val, 0);
+		if (!l) {
+			rpc_fault(ctx, 500, "Internal Server Error, line %d", 
+						ctx->line_no);
+			goto err;
+		}
+		break;
+		
+	case 'S':
+		sp = va_arg(*ap, str*);
+		l = new_chunk_escape(sp, 0);
+		if (!l) {
+			rpc_fault(ctx, 500, "Internal Server Error, line %d", 
+							ctx->line_no);
+			goto err;
+		}
+		break;
+		
+	default:
+		rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)", fmt);
+		ERR("Invalid formatting character\n");
+		goto err;
+	}
+
+	l->flags |= CHUNK_POSITIONAL;
+	append_chunk(ctx, l);
+	return 0;
+ err:
+	return -1;
+}
+
+
+static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
+{
+	void** void_ptr;
+	va_list ap;
+	str s = {"", 0};
+	struct text_chunk* l;
+
+	va_start(ap, fmt);
+	while(*fmt) {
+		if (*fmt == '{') {
+			void_ptr = va_arg(ap, void**);
+			l = new_chunk(&s);
+			if (!l) {
+				rpc_fault(ctx, 500, "Internal Server Error");
+				goto err;
+			}
+			l->ctx=ctx;
+			append_chunk(ctx, l);
+			*void_ptr = l;
+		} else {
+			if (print_value(ctx, *fmt, &ap) < 0) goto err;
+		}
+		fmt++;
+	}
+	va_end(ap);
+	return 0;
+ err:
+	va_end(ap);
+	return -1;
+}
+
+#define RPC_BUF_SIZE 1024
+
+static int rpc_struct_printf(struct text_chunk* c, char* name, char* fmt, ...)
+{
+	int n, buf_size;
+	char* buf;
+	va_list ap;
+	str s, nm;
+	struct text_chunk* l, *m;
+	rpc_ctx_t* ctx;
+	
+	ctx=(rpc_ctx_t*)c->ctx;
+	buf = (char*)pkg_malloc(RPC_BUF_SIZE);
+	if (!buf) {
+		rpc_fault(ctx,  500, "Internal Server Error (No memory left)");
+		ERR("No memory left\n");
+		return -1;
+	}
+	
+	buf_size = RPC_BUF_SIZE;
+	while (1) {
+		     /* Try to print in the allocated space. */
+		va_start(ap, fmt);
+		n = vsnprintf(buf, buf_size, fmt, ap);
+		va_end(ap);
+		     /* If that worked, return the string. */
+		if (n > -1 && n < buf_size) {
+			nm.s = name;
+			nm.len = strlen(name);
+			m = new_chunk_escape(&nm, 1); /* Escape all characters, including : and , */
+			if (!m) {
+				rpc_fault(ctx, 500, "Internal Server Error");
+				goto err;
+			}
+
+			s.s = buf;
+			s.len = n;
+			l = new_chunk_escape(&s, 1);
+			if (!l) {
+				rpc_fault(ctx, 500, "Internal Server Error");
+				free_chunk(m);
+				ERR("Error while creating text_chunk structure");
+				goto err;
+			}
+			
+			l->flags |= CHUNK_MEMBER_VALUE;
+			l->next = c->next;
+			c->next = l;
+			if (c == ctx->last) ctx->last = l;
+
+			m->flags |= CHUNK_MEMBER_NAME;
+			m->next = c->next;
+			c->next = m;
+			if (c == ctx->last) ctx->last = m;
+			return 0;
+		}
+		     /* Else try again with more space. */
+		if (n > -1) {   /* glibc 2.1 */
+			buf_size = n + 1; /* precisely what is needed */
+		} else {          /* glibc 2.0 */
+			buf_size *= 2;  /* twice the old size */
+		}
+		if ((buf = pkg_realloc(buf, buf_size)) == 0) {
+			rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
+			ERR("No memory left\n");
+			goto err;
+		}
+	}
+	return 0;
+ err:
+	if (buf) pkg_free(buf);
+	return -1;
+}
+
+
+static int rpc_printf(rpc_ctx_t* ctx, char* fmt, ...)
+{
+	int n, buf_size;
+	char* buf;
+	va_list ap;
+	str s;
+	struct text_chunk* l;
+
+	buf = (char*)pkg_malloc(RPC_BUF_SIZE);
+	if (!buf) {
+		rpc_fault(ctx,  500, "Internal Server Error (No memory left)");
+		ERR("No memory left\n");
+		return -1;
+	}
+	
+	buf_size = RPC_BUF_SIZE;
+	while (1) {
+		     /* Try to print in the allocated space. */
+		va_start(ap, fmt);
+		n = vsnprintf(buf, buf_size, fmt, ap);
+		va_end(ap);
+		     /* If that worked, return the string. */
+		if (n > -1 && n < buf_size) {
+			s.s = buf;
+			s.len = n;
+			l = new_chunk_escape(&s, 0);
+			if (!l) {
+				rpc_fault(ctx, 500, "Internal Server Error");
+				ERR("Error while creating text_chunk structure");
+				goto err;
+			}
+			append_chunk(ctx, l);
+			pkg_free(buf);
+			return 0;
+		}
+		     /* Else try again with more space. */
+		if (n > -1) {   /* glibc 2.1 */
+			buf_size = n + 1; /* precisely what is needed */
+		} else {          /* glibc 2.0 */
+			buf_size *= 2;  /* twice the old size */
+		}
+		if ((buf = pkg_realloc(buf, buf_size)) == 0) {
+			rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
+			ERR("No memory left\n");
+			goto err;
+		}
+	}
+	return 0;
+ err:
+	if (buf) pkg_free(buf);
+	return -1;
+}
+
+
+static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
+{
+	struct text_chunk* l;
+	struct rpc_struct* s;
+	int* int_ptr;
+	char** char_ptr;
+	str* str_ptr;
+	double* double_ptr;
+	void** void_ptr;
+	int read;
+	str line;
+
+	va_list ap;
+	va_start(ap, fmt);
+
+	read = 0;
+	while(*fmt) {
+		if (read_line(&line.s, &line.len, &ctx->read_h) < 0) {
+			va_end(ap);
+			return read;
+		}
+		ctx->line_no++;
+
+		switch(*fmt) {
+		case 'b': /* Bool */
+		case 't': /* Date and time */
+		case 'd': /* Integer */
+			if (!line.len) {
+				rpc_fault(ctx, 400, "Invalid parameter value on line %d", 
+									ctx->line_no);
+				goto error;
+			}
+			int_ptr = va_arg(ap, int*);
+			*int_ptr = strtol(line.s, 0, 0);
+			break;
+
+		case 'f': /* double */
+			if (!line.len) {
+				rpc_fault(ctx, 400, "Invalid parameter value on line %d", 
+								ctx->line_no);
+				goto error;
+			}
+			double_ptr = va_arg(ap, double*);
+			*double_ptr = strtod(line.s, 0);
+			break;
+			
+		case 's': /* zero terminated string */
+		case 'S': /* str structure */
+			l = new_chunk_unescape(&line);
+			if (!l) {
+				rpc_fault(ctx, 500, "Internal Server Error");
+				ERR("Not enough memory\n");
+				goto error;
+			}
+			     /* Make sure it gets released at the end */
+			l->next = ctx->strs;
+			ctx->strs = l;
+
+			if (*fmt == 's') {
+				char_ptr = va_arg(ap, char**);
+				*char_ptr = l->s.s;
+			} else {
+				str_ptr = va_arg(ap, str*);
+				*str_ptr = l->s;
+			}
+			break;
+
+		case '{':
+			void_ptr = va_arg(ap, void**);
+			s = new_struct(ctx, &line);
+			if (!s) goto error;
+			s->next = ctx->structs;
+			ctx->structs = s;
+			*void_ptr = s;
+			break;
+
+		default:
+			ERR("Invalid parameter type in formatting string: %c\n", *fmt);
+			rpc_fault(ctx, 500, "Server Internal Error (Invalid Formatting Character '%c')", *fmt);
+			goto error;
+		}
+		fmt++;
+		read++;
+	}
+	va_end(ap);
+	return read;
+
+ error:
+	va_end(ap);
+	return -read;
+}
+
+
+
+static int rpc_struct_add(struct text_chunk* s, char* fmt, ...)
+{
+	static char buf[MAX_LINE_BUFFER];
+	str st, *sp;
+	va_list ap;
+	struct text_chunk* m, *c;
+	rpc_ctx_t* ctx;
+
+	ctx=(rpc_ctx_t*)s->ctx;
+	va_start(ap, fmt);
+	while(*fmt) {
+		     /* Member name escaped */
+		st.s = va_arg(ap, char*);
+		st.len = strlen(st.s);
+		m = new_chunk_escape(&st, 1); /* Escape all characters, including : and , */
+		if (!m) {
+			rpc_fault(ctx, 500, "Internal Server Error");
+			goto err;
+		}
+		m->flags |= CHUNK_MEMBER_NAME;
+		
+		switch(*fmt) {
+		case 'd':
+		case 't':
+			st.s = int2str(va_arg(ap, int), &st.len);
+			c = new_chunk(&st);
+			break;
+			
+		case 'f':
+			st.s = buf;
+			st.len = snprintf(buf, 256, "%f", va_arg(ap, double));
+			if (st.len < 0) {
+				rpc_fault(ctx, 400, "Error While Converting double");
+				ERR("Error while converting double\n");
+				goto err;
+			}
+			c = new_chunk(&st);
+			break;
+			
+		case 'b':
+			st.len = 1;
+			st.s = ((va_arg(ap, int) == 0) ? "0" : "1");
+			c = new_chunk(&st);
+			break;
+			
+		case 's':
+			st.s = va_arg(ap, char*);
+			st.len = strlen(st.s);
+			c = new_chunk_escape(&st, 1);
+			break;
+			
+		case 'S':
+			sp = va_arg(ap, str*);
+			c = new_chunk_escape(sp, 1);
+			break;
+			
+		default:
+			rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)",
+					*fmt);
+			ERR("Invalid formatting character\n");
+			goto err;
+		}
+
+		if (!c) {
+			rpc_fault(ctx, 500, "Internal Server Error");
+			goto err;
+		}
+		c->flags |= CHUNK_MEMBER_VALUE;
+		c->next = s->next;
+		s->next = c;
+		if (s == ctx->last) ctx->last = c;
+
+		m->next = s->next;
+		s->next = m;
+		if (s == ctx->last) ctx->last = m;
+
+		fmt++;
+	}
+	va_end(ap);
+	return 0;
+ err:
+	if (m) free_chunk(m);
+	va_end(ap);
+	return -1;
+}
+
+
+static int find_member(struct text_chunk** value, struct rpc_struct* s, str* member_name)
+{
+	struct text_chunk* n, *v;
+
+	n = s->names;
+	v = s->values;
+	while(n) {
+		if (member_name->len == n->s.len &&
+		    !strncasecmp(member_name->s, n->s.s, n->s.len)) {
+			if (n->flags & CHUNK_SEEN) goto skip;
+			else {
+				*value = v;
+				n->flags |= CHUNK_SEEN;
+				return 0;
+			}
+		}
+
+	skip:
+		n = n->next;
+		v = v->next;
+	}
+	return 1;
+}
+
+
+static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...)
+{
+	struct text_chunk* val;
+	va_list ap;
+	int* int_ptr;
+	double* double_ptr;
+	char** char_ptr;
+	str* str_ptr;
+	str member_name;
+	int ret, read;
+
+	read = 0;
+	va_start(ap, fmt);
+	while(*fmt) {
+		member_name.s = va_arg(ap, char*);
+		member_name.len = strlen(member_name.s);
+		ret = find_member(&val, s, &member_name);
+		if (ret > 0) {
+			va_end(ap);
+			return read;
+		}
+		
+		switch(*fmt) {
+		case 'b': /* Bool */
+		case 't': /* Date and time */
+		case 'd': /* Integer */
+			int_ptr = va_arg(ap, int*);
+			if (!val->s.len) {
+				rpc_fault(s->ctx, 400, "Invalid Parameter Value");
+				goto error;
+			}
+			     /* String in text_chunk is always zero terminated */
+			*int_ptr = strtol(val->s.s, 0, 0);
+			break;
+
+		case 'f': /* double */
+			double_ptr = va_arg(ap, double*);
+			if (!val->s.len) {
+				rpc_fault(s->ctx, 400, "Invalid Parameter Value");
+				goto error;
+			}
+			     /* String in text_chunk is always zero terminated */
+			*double_ptr = strtod(val->s.s, 0);
+			break;
+			
+		case 's': /* zero terminated string */
+			char_ptr = va_arg(ap, char**);
+			     /* String in text_chunk is always zero terminated */
+			*char_ptr = val->s.s;
+			break;
+
+		case 'S': /* str structure */
+			str_ptr = va_arg(ap, str*);
+			str_ptr->len = strlen(str_ptr->s);
+			*str_ptr = val->s;
+			break;
+		default:
+			rpc_fault(s->ctx, 500, "Invalid character in formatting string '%c'", *fmt);
+			ERR("Invalid parameter type in formatting string: %c\n", *fmt);
+			goto error;
+		}
+		fmt++;
+		read++;
+	}
+	va_end(ap);
+	return read;
+ error:
+	va_end(ap);
+	return -read;
+}
+
+#endif

+ 51 - 0
modules/binrpc/fifo_server.h

@@ -0,0 +1,51 @@
+/*
+ * $Id: fifo_server.h,v 1.1 2006/02/23 19:57:31 andrei Exp $
+ *
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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 _FIFO_SERVER_H
+#define _FIFO_SERVER_H
+
+#include <stdio.h>
+
+#define CMD_SEPARATOR ':'
+
+extern char* fifo_dir;
+extern int   fifo_reply_retries;
+extern int   fifo_reply_wait;
+
+/* Initialize FIFO server data structures */
+int init_fifo_fd(char* fifo, int fifo_mode, int fifo_uid, int fifo_gid,
+					int* wfd);
+
+int fifo_process(char* msg_buf, int size, int* bytes_need, void *sh, void** s);
+/* memory deallocation */
+void destroy_fifo(int read_fd, int w_fd, char* fname);
+int fifo_rpc_init();
+
+#endif

+ 192 - 0
modules/binrpc/init_socks.c

@@ -0,0 +1,192 @@
+/*
+ * $Id: init_socks.c,v 1.1 2006/02/23 19:57:31 andrei Exp $
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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
+ */
+/* History:
+ * --------
+ *  2006-02-14  created by andrei
+ *  2007        ported to libbinrpc (bpintea)
+ */
+
+#include "init_socks.h"
+#include "../../dprint.h"
+#include "../../ip_addr.h"
+#include "../../resolve.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h> /*IPTOS_LOWDELAY*/
+#include <netinet/tcp.h>
+#include <sys/un.h>
+#include <unistd.h> /* unlink */
+#include <sys/stat.h> /* chmod */
+#include <fcntl.h>
+
+#define LISTEN_BACKLOG	128
+
+
+/* returns -1 on error */
+static int set_non_blocking(int s)
+{
+	int flags;
+	/* non-blocking */
+	flags=fcntl(s, F_GETFL);
+	if (flags==-1){
+		LOG(L_ERR, "ERROR: set_non_blocking: fnctl failed: (%d) %s\n",
+				errno, strerror(errno));
+		goto error;
+	}
+	if (fcntl(s, F_SETFL, flags|O_NONBLOCK)==-1){
+		LOG(L_ERR, "ERROR: set_non_blocking: fcntl: set non-blocking failed:"
+				" (%d) %s\n", errno, strerror(errno));
+		goto error;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+
+static inline int init_srv_sock(brpc_addr_t *addr)
+{
+	int sockfd;
+
+	if ((sockfd = brpc_socket(addr, /*block.*/false, /*named*/true)) < 0) {
+		ERR("failed to bind listen socket: %s [%d].\n", brpc_strerror(),
+				brpc_errno);
+		goto err;
+	}
+	/* listen on stream sockets */
+	if ((BRPC_ADDR_TYPE(addr)==SOCK_STREAM) && 
+			(listen(sockfd, LISTEN_BACKLOG)==-1)){
+		ERR("listen: %s [%d]\n", strerror(errno), errno);
+		goto err;
+	}
+	return sockfd;
+err:
+	if (0 <= sockfd)
+		close(sockfd);
+	return -1;
+}
+
+/* opens, binds and listens-on a control unix socket of type 'type' 
+ * it will change the permissions to perm, if perm!=0
+ * and the ownership to uid.gid if !=-1
+ * returns socket fd or -1 on error */
+int init_unix_sock(brpc_addr_t* addr, int perm, int uid, int gid)
+{
+	int sockfd;
+	char *name;
+
+	if ((sockfd = init_srv_sock(addr)) < 0)
+		return sockfd;
+
+	name = BRPC_ADDR_UN(addr)->sun_path;
+	/* then the permissions */
+	if (perm){ /* mode==0 doesn't make sense, nobody can read/write */
+		if (chmod(name, perm)<0){
+			LOG(L_ERR, "ERROR: init_unix_sock: failed to change the"
+					" permissions for %s to %04o: %s[%d]\n",
+					name, perm, strerror(errno), errno);
+			goto error;
+		}
+	}
+	/* try to change ownership */
+	if ((uid!=-1) || (gid!=-1)){
+		if (chown(name, uid, gid)<0){
+			LOG(L_ERR, "ERROR: init_unix_sock: failed to change the"
+					" owner/group for %s to %d.%d: %s[%d]\n",
+					name, uid, gid, strerror(errno), errno);
+			goto error;
+		}
+	}
+	return sockfd;
+error:
+	close(sockfd);
+	return -1;
+}
+
+
+/* opens, binds and listens-on a control tcp socket
+ * returns socket fd or -1 on error */
+int init_tcpudp_sock(brpc_addr_t *addr)
+{
+	return init_srv_sock(addr);
+}
+
+/* set all socket/fd options:  disable nagle, tos lowdelay, non-blocking
+ * return -1 on error */
+//int init_sock_opt(int s, enum socket_protos type)
+int init_sock_opt(int s, sa_family_t domain, int socktype)
+{
+	int optval;
+#ifdef DISABLE_NAGLE
+	int flags;
+#if 0
+	struct protoent* pe;
+#endif
+#endif
+
+	if (domain != PF_LOCAL) {
+#ifdef DISABLE_NAGLE
+		if (socktype == SOCK_STREAM) {
+			flags=1;
+#if 0
+			/* TODO: "getprotobyname() [...] is expensive, slow, and 
+			 * generally useless" - check out if "useless". */
+			if (tcp_proto_no==-1){ /* if not already set */
+				pe=getprotobyname("tcp");
+				if (pe!=0){
+					tcp_proto_no=pe->p_proto;
+				}
+			}
+			if ( (tcp_proto_no!=-1) && (setsockopt(s, tcp_proto_no,
+#endif
+			if ((setsockopt(s, IPPROTO_TCP, TCP_NODELAY, 
+					&flags, sizeof(flags))) < 0) {
+				LOG(L_WARN, "WARNING: init_sock_opt: could not disable"
+							" Nagle: %s\n", strerror(errno));
+			}
+		}
+#endif
+		/* tos*/
+		optval = IPTOS_LOWDELAY;
+		if (setsockopt(s, IPPROTO_IP, IP_TOS, (void*)&optval,
+						sizeof(optval)) ==-1){
+			LOG(L_WARN, "WARNING: init_sock_opt: setsockopt tos: %s\n",
+					strerror(errno));
+			/* continue since this is not critical */
+		}
+	}
+	if (set_non_blocking(s)==-1){
+		LOG(L_ERR, "ERROR: init_sock_opt: set non blocking failed\n");
+	}
+	return 0;
+}

+ 80 - 0
modules/binrpc/init_socks.h

@@ -0,0 +1,80 @@
+/*
+ * $Id: init_socks.h,v 1.1 2006/02/23 19:57:31 andrei Exp $
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ * Copyright (C) 2007 iptego GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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
+ */
+/* History:
+ * --------
+ *  2006-02-14  created by andrei
+ */
+
+#ifndef _init_socks_h
+#define _init_socks_h
+#include <sys/un.h>
+#include "../../ip_addr.h"
+
+#include <binrpc.h>
+
+#if 0
+enum socket_protos	{	UNKNOWN_SOCK=0, UDP_SOCK, TCP_SOCK, 
+						UNIXS_SOCK, UNIXD_SOCK
+#ifdef USE_FIFO
+							, FIFO_SOCK
+#endif
+};
+#endif
+
+#ifndef PF_MAX
+#define PF_MAX	0xF1F0
+#endif
+
+#define PF_FIFO	(PF_MAX + 1)
+
+
+int init_unix_sock(brpc_addr_t* addr, int perm, int uid, int gid);
+int init_tcpudp_sock(brpc_addr_t *addr);
+int init_sock_opt(int s, sa_family_t domain, int socktype);
+
+
+inline static char* socket_proto_name(brpc_addr_t *addr)
+{
+	switch (addr->domain) {
+		case PF_LOCAL:
+			return (addr->socktype == SOCK_STREAM) ? "unix_stream" : 
+					"unix_datagram";
+		case PF_INET:
+			return (addr->socktype == SOCK_STREAM) ? "IPv4_TCP" : "IPv4_UDP";
+		case PF_INET6:
+			return (addr->socktype == SOCK_STREAM) ? "IPv6_TCP" : "IPv6_UDP";
+#ifdef USE_FIFO
+		case PF_FIFO:
+			return "fifo";
+#endif
+	}
+	BUG("unknown address type %d:%d.\n", addr->domain, addr->socktype);
+	return "<unknown>";
+}
+
+#endif

+ 918 - 0
modules/binrpc/io_listener.c

@@ -0,0 +1,918 @@
+/*
+ * $Id: io_listener.c,v 1.5 2006/11/17 20:07:36 andrei Exp $
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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
+ */
+/* History:
+ * --------
+ *  2006-02-15  created by andrei
+ *  2007        ported to libbinrpc (bpintea)
+ */
+
+#include "../../globals.h"
+#include "../../pt.h"  /* process_count */
+#include "../../timer.h"
+#include "../../timer_ticks.h"
+#include "../../tsend.h"
+#include "../../mem/mem.h"
+#include "../../rpc.h" /* who & ls rpcs */
+#include "../../ut.h"
+#include "../../dprint.h"
+#include "../../pass_fd.h"
+#ifdef EXTRA_DEBUG
+#include <assert.h>
+#endif
+
+#include "libbinrpc_wrapper.h"
+#include "proctab.h"
+#include "ctrl_socks.h"
+//#include "binrpc_run.h"
+#ifdef USE_FIFO
+#include "fifo_server.h"
+#endif
+
+#include "io_listener.h"
+
+#define HANDLE_IO_INLINE
+#include "../../io_wait.h"
+#include <fcntl.h> /* required by io_wait.h if SIGIO_RT is used */
+#include <sys/uio.h> /* iovec */
+
+#define MAX_IO_READ_CONNECTIONS		128 /* FIXME: make it a config var */
+#define IO_STREAM_CONN_TIMEOUT		S_TO_TICKS(120)
+#define IO_LISTEN_TIMEOUT			10 /* in s,  how often the timer 
+										  will be run */
+
+#define IO_LISTEN_TX_TIMEOUT	10 /* ms */
+#define DGRAM_BUF_SIZE	65535
+#define STREAM_BUF_SIZE	65535
+
+
+/* 0 has a special meaning, don't use it (see io_wait.h)*/
+enum fd_type { F_T_RESERVED=0,  F_T_CTRL_DGRAM, F_T_CTRL_STREAM,
+				 F_T_READ_STREAM
+#ifdef USE_FIFO
+				, F_T_FIFO
+#endif
+				, F_T_FDPASS
+};
+
+
+struct stream_connection{
+	struct stream_connection* next;
+	struct stream_connection* prev;
+	struct ctrl_socket* parent;
+	brpc_strd_t rds; /* ReaD State */
+	ticks_t expire;
+	union sockaddr_u from;
+};
+
+/* is there an extra process to manage connections */
+int have_connection_listener = 0;
+
+
+typedef int (*send_ev_f)(void* send_h , struct iovec* v, size_t count);
+
+
+
+static io_wait_h io_h;
+static int io_read_connections=0;
+static struct stream_connection stream_conn_lst; /* list head */
+
+static struct stream_connection* s_conn_new(int sock, 
+											struct ctrl_socket* cs,
+											union sockaddr_u* from)
+{
+	struct stream_connection* s_c;
+	
+	s_c=pkg_malloc(sizeof(struct stream_connection));
+	if (s_c){
+		memset(s_c, 0, sizeof(struct stream_connection));
+		brpc_strd_init(&s_c->rds, sock);
+		s_c->expire=get_ticks_raw()+IO_STREAM_CONN_TIMEOUT;
+		s_c->from=*from;
+		s_c->parent=cs;
+	}
+	return s_c;
+}
+
+
+#define s_conn_add(s_c) clist_append(&stream_conn_lst, s_c, next, prev)
+
+
+
+inline static void s_conn_rm(struct stream_connection* sc)
+{
+	clist_rm(sc, next, prev);
+	pkg_free(sc);
+	io_read_connections--;
+}
+
+
+
+
+/*
+ * sends on a "disconnected" socket/fd (e.g. not connected udp socket,
+ *  unix datagram socket)
+ * returns: number  of bytes written on success,
+ *          <0  on error (-1 tsend* error, -2 packet too big)
+ */
+inline static int sendv_disc(struct send_handle* sh, struct iovec* v,
+								size_t count)
+{
+	char buf[DGRAM_BUF_SIZE];
+	char* p;
+	char* end;
+	int r;
+	
+	p=buf;
+	end=p+DGRAM_BUF_SIZE;
+	for (r=0; r<count; r++){
+		if ((p+v[r].iov_len)>end) 
+			goto error_overflow;
+		memcpy(p, v[r].iov_base, v[r].iov_len);
+		p+=v[r].iov_len;
+	}
+	return tsend_dgram(sh->fd, buf, (int)(p-buf), &sh->from.sa_in.s,
+						sh->from_len, IO_LISTEN_TX_TIMEOUT);
+error_overflow:
+	return -2;
+}
+
+
+
+/* returns: number of bytes written on success,
+ *          <0 on error (-1 send error, -2 too big)
+ */
+int sock_send_v(void *h, struct iovec* v, size_t count)
+{
+	struct send_handle* sh;
+	
+	sh=(struct send_handle*)h;
+	if (sh->type==S_CONNECTED)
+		return tsend_dgram_ev(sh->fd, v, count, IO_LISTEN_TX_TIMEOUT);
+	else
+		return sendv_disc(sh, v, count);
+};
+
+
+
+void io_listen_loop(struct ctrl_socket* cs_lst)
+{
+	int max_fd_no;
+	char* poll_err;
+	int poll_method;
+	struct ctrl_socket *cs;
+	int type;
+	
+	clist_init(&stream_conn_lst, next, prev);
+	max_fd_no=get_max_open_fds();
+	/* choose/fix the poll method */
+	/* FIXME: make it a config param? */
+#if USE_TCP
+	poll_method=tcp_poll_method; /* try to resue the tcp poll method */
+	poll_err=check_poll_method(poll_method);
+#else
+	poll_method = 0; /* make check for TCP poll method fail */
+	poll_err = NULL;
+#endif
+	
+	/* set an appropiate poll method */
+	if (poll_err || (poll_method==0)){
+		poll_method=choose_poll_method();
+		if (poll_err){
+			LOG(L_ERR, "ERROR: io_listen_loop: %s, using %s instead\n",
+					poll_err, poll_method_name(poll_method));
+		}else{
+			LOG(L_INFO, "io_listen_loop: using %s as the io watch method"
+					" (auto detected)\n", poll_method_name(poll_method));
+		}
+	}else{
+			LOG(L_INFO, "io_listen_loop:  using %s io watch method (config)\n",
+					poll_method_name(poll_method));
+	}
+	
+	if (init_io_wait(&io_h, max_fd_no, poll_method)<0)
+		goto error;
+	/* add all the sockets we listen on for connections */
+	for (cs=cs_lst; cs; cs=cs->next){
+		if (cs->fd < 0) {
+			/* stream address is disabled (handled by dedicated process) */
+#ifdef EXTRA_DEBUG
+			assert(BRPC_ADDR_TYPE(&cs->addr) == SOCK_STREAM);
+#endif
+			continue;
+		}
+		switch (cs->addr.domain) {
+			case PF_LOCAL:
+				if (cs->p_proto == P_FDPASS) {
+					type = F_T_FDPASS;
+					break;
+				}
+				/* no breaks! */
+			case PF_INET:
+			case PF_INET6:
+				switch (cs->addr.socktype) {
+					case SOCK_DGRAM:
+						type=F_T_CTRL_DGRAM;
+						break;
+					case SOCK_STREAM:
+						type=F_T_CTRL_STREAM;
+						break;
+					default:
+						BUG("unknown control socket transport [%d:%d]\n", 
+								cs->addr.domain, cs->addr.socktype);
+						goto error;
+				}
+				break;
+
+#ifdef USE_FIFO
+			case PF_FIFO:
+				type=F_T_FIFO; /* special */
+				cs->data=s_conn_new(cs->fd, cs, 
+						/* reuse stream conn */
+						(union sockaddr_u *)&cs->addr.sockaddr);
+				if (cs->data==0){
+					LOG(L_ERR, "ERROR: io_listen_loop: out of memory\n");
+					goto error;
+				}
+				break;
+#endif
+			default:
+				BUG("unknown socket domain %d.\n", cs->addr.domain);
+				goto error;
+		} /* outermost switch */
+		
+		DEBUG("io_listen_loop: adding socket %d, type %d, transport"
+				" [%d:%d] (%s)\n", cs->fd, type, cs->addr.domain, 
+				cs->addr.socktype, CTRLSOCK_URI(cs));
+		if (io_watch_add(&io_h, cs->fd, POLLIN, type, cs)<0){
+			LOG(L_CRIT, "ERROR: io_listen_loop: init: failed to add"
+					"listen socket to the fd list\n");
+			goto error;
+		}
+	}
+	/* main loop */
+	switch(io_h.poll_method){
+		case POLL_POLL:
+			while(1){
+				io_wait_loop_poll(&io_h, IO_LISTEN_TIMEOUT, 0);
+			}
+			break;
+#ifdef HAVE_SELECT
+		case POLL_SELECT:
+			while(1){
+				io_wait_loop_select(&io_h, IO_LISTEN_TIMEOUT, 0);
+			}
+			break;
+#endif
+#ifdef HAVE_SIGIO_RT
+		case POLL_SIGIO_RT:
+			while(1){
+				io_wait_loop_sigio_rt(&io_h, IO_LISTEN_TIMEOUT);
+			}
+			break;
+#endif
+#ifdef HAVE_EPOLL
+		case POLL_EPOLL_LT:
+			while(1){
+				io_wait_loop_epoll(&io_h, IO_LISTEN_TIMEOUT, 0);
+			}
+			break;
+		case POLL_EPOLL_ET:
+			while(1){
+				io_wait_loop_epoll(&io_h, IO_LISTEN_TIMEOUT, 1);
+			}
+			break;
+#endif
+#ifdef HAVE_KQUEUE
+		case POLL_KQUEUE:
+			while(1){
+				io_wait_loop_kqueue(&io_h, IO_LISTEN_TIMEOUT, 0);
+			}
+			break;
+#endif
+#ifdef HAVE_DEVPOLL
+		case POLL_DEVPOLL:
+			while(1){
+				io_wait_loop_devpoll(&io_h, IO_LISTEN_TIMEOUT, 0);
+			}
+			break;
+#endif
+		default:
+			LOG(L_CRIT, "BUG: io_listen_loop: no support for poll method "
+					" %s (%d)\n", 
+					poll_method_name(io_h.poll_method), io_h.poll_method);
+			goto error;
+	}
+/* should never reach this point under normal (non-error) circumstances */
+error:
+	CRIT("io_listen_loop exiting ...\n");
+}
+
+
+
+/* handles an io event on one of the watched dgram connections
+ * (it can read the whole packet)
+ * 
+ * params: cs - pointer to the control socket for which we have an io ev.
+ * returns:  handle_* return convention:
+ *         -1 on error, or when we are not interested any more on reads
+ *            from this fd (e.g.: we are closing it )
+ *          0 on EAGAIN or when by some other way it is known that no more 
+ *            io events are queued on the fd (the receive buffer is empty).
+ *            Usefull to detect when there are no more io events queued for
+ *            sigio_rt, epoll_et, kqueue.
+ *         >0 on successfull read from the fd (when there might be more io
+ *            queued -- the receive buffer might still be non-empty)
+ *
+ */
+/* TODO: udpate comment above */
+static int handle_dgram(struct ctrl_socket* cs)
+{
+	struct reply_to sndto;
+	brpc_t *raw_req;
+
+	sndto.fd = cs->fd;
+	sndto.addr = cs->addr;
+	raw_req = brpc_recvfrom(cs->fd, &sndto.addr, 0);
+	if (! raw_req) {
+		ERR("failed to received new datagram (on %s): %s [%d].\n", 
+				CTRLSOCK_URI(cs), brpc_strerror(), brpc_errno);
+		return -1;
+	}
+	DEBUG("new datagram received (on %s).\n", CTRLSOCK_URI(cs));
+#ifdef USE_FIFO
+	if (cs->p_proto==P_FIFO)
+		fifo_process((char*)buf, bytes, &bytes_needed, &sh, &saved_state);
+	else
+#endif
+		return (binrpc_run(raw_req, sndto) == BRPC_RUN_SUCCESS) ? 0 : -1;
+}
+
+/**
+ * Add a socket to a connections list and insert it into IO monitored sockets.
+ */
+static inline int add_stream_connection(int new_sock, struct ctrl_socket *cs, 
+		union sockaddr_u *from)
+{
+	struct stream_connection* s_conn;
+	/* add socket to the list */
+	s_conn=s_conn_new(new_sock, cs, from);
+	if (s_conn){
+		s_conn_add(s_conn);
+		io_watch_add(&io_h, s_conn->rds.fd, POLLIN, F_T_READ_STREAM, s_conn);
+	}else{
+		LOG(L_ERR, "ERROR: io listen: handle_new_connect:"
+				" s_conn_new failed\n");
+		return -1;
+	}
+	io_read_connections++;
+	return 0;
+}
+
+static inline void del_stream_connection(struct stream_connection* s_c,int idx)
+{
+	struct ctrl_socket *cs;
+
+	io_watch_del(&io_h, s_c->rds.fd, idx, IO_FD_CLOSING);
+	close(s_c->rds.fd);
+	/* TODO: is have_connection_listener necessary to check?! */
+	if (have_connection_listener 
+			&& (BRPC_ADDR_TYPE(&s_c->parent->addr) == SOCK_STREAM)) {
+		/* stream connection for passed descriptor*/
+		/* FIXME: obtain the ref to P_FDPASS cs more efficiently */
+		for (cs = ctrl_sock_lst; cs; cs = cs->next) {
+			if (cs->p_proto != P_FDPASS)
+				continue;
+			if (send_all(cs->fd, "", 1) < 1) {
+				WARN("failed to signal connection termination to binrpc "
+						"connections listener: %s [%d].\n", strerror(errno), 
+						errno);
+#ifdef EXTRA_DEBUG
+				abort();
+#endif
+			}
+			break;
+		}
+	}
+	s_conn_rm(s_c);
+
+}
+
+/* handles an new connect on one of the watched stream connections
+ * 
+ * params: cs - pointer to the control socket for which we have an io ev.
+ * returns:  handle_* return convention:
+ *         -1 on error, or when we are not interested any more on accepts
+ *            from this fd (e.g.: we are closing it )
+ *          0 on EAGAIN or when by some other way it is known that no more 
+ *            io events are queued on the fd (the receive buffer is empty).
+ *            Usefull to detect when there are no more io events queued for
+ *            sigio_rt, epoll_et, kqueue.
+ *         >0 on successfull accept from the fd (when there might be more io
+ *            queued -- the receive buffer might still be non-empty)
+ *
+ */
+static int handle_new_connect(struct ctrl_socket* cs)
+{
+	int ret;
+	union sockaddr_u from;
+	unsigned int from_len;
+	int new_sock;
+	size_t connections;
+	ssize_t worker = 0 /*4gcc*/;
+	struct ctrl_socket *w_cs;
+
+	DEBUG("new incomming connection on %s.\n", CTRLSOCK_URI(cs));
+	from_len = cs->addr.addrlen; /* can only be same address length */
+again:
+	new_sock=accept(cs->fd, &from.sa_in.s, &from_len);
+	if (new_sock==-1){
+		if ((errno==EAGAIN)||(errno==EWOULDBLOCK)){
+			ret=0;
+			goto skip;
+		}else if (errno==EINTR){
+			goto again;
+		}
+		LOG(L_ERR, "ERROR: io_listen: handle_new_connect:"
+				" error while accepting connection on %s: [%d] %s\n",
+				CTRLSOCK_URI(cs), errno, strerror(errno));
+		goto error;
+	}
+	ret=1;
+	if (have_connection_listener) {
+		if ((worker = pt_least_loaded(&connections)) < 0)
+			goto close;
+	} else {
+		connections = io_read_connections;
+	}
+	if (connections>=MAX_IO_READ_CONNECTIONS){
+		LOG(L_ERR, "ERROR: io listen: maximum number of connections"
+				" exceeded: %d/%d\n",
+				io_read_connections, MAX_IO_READ_CONNECTIONS);
+		goto close; /* success because accept was successful */
+	}
+	if (init_sock_opt(new_sock, cs->addr.domain, cs->addr.socktype)<0){
+		LOG(L_ERR, "ERROR: io listen: handle_new_connect:"
+				" init_sock_opt failed\n");
+		goto close;
+	}
+	if (have_connection_listener) {
+		/* pass socket to worker */
+		if (! (w_cs = pt_update_load(worker, /*inc*/1))) {
+			BUG("invalid (NULL) child FDPASS control socket.\n");
+#ifdef EXTRA_DEBUG
+			abort();
+#endif
+			goto close;
+		}
+		if (send_fd(w_cs->fd, &cs, sizeof(cs/*ptr!*/), new_sock)<sizeof(cs)) {
+			ERR("failed to pass connection descriptor: %s [%d].\n", 
+					strerror(errno), errno);
+			pt_update_load(worker, /*dec*/-1);
+			goto close;
+		}
+		close(new_sock); /* no longer needed in this process */
+		DEBUG("new connection dispatched to binrpc worker PID#%d.\n", 
+				pt_pid(worker));
+	} else {
+#if 0
+		/* TODO: XXX: FIXME: used uninitialized! */
+		//if (add_stream_connection(new_sock, w_cs, &from) < 0)
+#endif
+		if (add_stream_connection(new_sock, cs, &from) < 0)
+			goto close;
+	}
+	DEBUG("handle_stream read: new connection on %s\n", CTRLSOCK_URI(cs));
+skip:
+	return ret;
+close:
+	if (0 <= new_sock)
+		close(new_sock);
+	return ret;
+error:
+	return -1;
+}
+
+
+
+static int handle_read(struct stream_connection* s_c, int idx)
+{
+	uint8_t *buff;
+	size_t pkt_len;
+	struct reply_to sndto;
+	brpc_t *raw_req;
+#ifdef USE_FIFO
+	ssize_t bytes_processed, bytes_needed;
+#endif
+
+	memset((char *)&sndto, 0, sizeof(struct reply_to));
+	sndto.fd = s_c->rds.fd;
+
+	DEBUG("new packet (fragment) received on %s.\n", CTRLSOCK_URI(s_c->parent));
+	brpc_errno = 0;
+	if (! brpc_strd_read(&s_c->rds)) {
+		switch (brpc_errno) {
+			case 0:
+				DEBUG("connection FD:%d (on %s) closed%s.\n", s_c->rds.fd, 
+						CTRLSOCK_URI(s_c->parent), 
+						s_c->rds.offset ? " (in middle of packet)" :
+						" (cleanly)");
+				goto close_connection;
+			case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+			case EWOULDBLOCK:
+#endif
+				goto no_read;
+			default:
+				ERR("while reading connection %d (with %s): %s [%d].\n",
+						s_c->rds.fd, CTRLSOCK_URI(s_c->parent), 
+						brpc_strerror(), brpc_errno);
+				goto error_read;
+		}
+	} else {
+#ifdef USE_FIFO
+		if (s_c->parent->p_proto==P_FIFO)
+			bytes_processed=fifo_process((char*)r->proc, (int)(r->end-r->proc),
+										&bytes_needed, &sh, &s_c->saved_state);
+		else
+#endif
+		{
+			brpc_errno = 0;
+			while ((buff = brpc_strd_wirepkt(&s_c->rds, &pkt_len))) {
+				DEBUG("new packet buffer of size %zd.\n", pkt_len);
+				if (! (raw_req = brpc_raw(buff, pkt_len))) {
+					ERR("BINRPC raw request reception failed: %s [%d]; RPC "
+							"dropped.\n", brpc_strerror(), brpc_errno);
+					goto skip;
+				}
+				switch (binrpc_run(raw_req, sndto)) {
+					case BRPC_RUN_ABORT:
+						WARN("connection fatal error while serving on %s.\n",
+								CTRLSOCK_URI(s_c->parent));
+						goto close_connection;
+					case BRPC_RUN_VOID:
+						WARN("nothing sent back for request on %s.\n", 
+								CTRLSOCK_URI(s_c->parent));
+						break;
+					case BRPC_RUN_SUCCESS:
+						DEBUG("succes serving on %s.\n", 
+								CTRLSOCK_URI(s_c->parent));
+						break;
+					default:
+						BUG("invalid context state while serving on %s.\n", 
+								CTRLSOCK_URI(s_c->parent));
+						goto close_connection;
+				}
+			}
+			switch (brpc_errno) {
+				case 0:
+					DEBUG("partial read on %s.\n", CTRLSOCK_URI(s_c->parent));
+					goto skip;
+				case EMSGSIZE:
+					/* TODO: read pending bytes to save the connection */
+					ERR("packet to receive on connection %d (with %s) is too"
+							" large: shutding down connection.\n", 
+							s_c->rds.fd, CTRLSOCK_URI(s_c->parent));
+					goto close_connection;
+			}
+		}
+	}
+
+skip:
+	/* everything went fine, we just have to read more */
+	s_c->expire=get_ticks_raw()+IO_STREAM_CONN_TIMEOUT; /* update timeout*/
+	return 1;
+	
+no_read:
+	/* false alarm */
+	return 0;
+/* TODO: what's the subtle diffrence between the two? the while's till >0.. */
+close_connection:
+	del_stream_connection(s_c, idx);
+	return 0;
+error_read:
+	del_stream_connection(s_c, idx);
+	return -1;
+	/* close connection */
+}
+
+
+#ifdef USE_FIFO
+/* handles a read event on one of the fifos 
+ * 
+ * params: s_c - pointer to the stream_connection for which we have an io ev.
+ *         idx - index in the fd_array -> pass this to io_watch_del for 
+ *               faster deletes
+ * returns:  handle_* return convention:
+ *         -1 on error, or when we are not interested any more on reads
+ *            from this fd (e.g.: we are closing it )
+ *          0 on EAGAIN or when by some other way it is known that no more 
+ *            io events are queued on the fd (the receive buffer is empty).
+ *            Usefull to detect when there are no more io events queued for
+ *            sigio_rt, epoll_et, kqueue.
+ *         >0 on successfull read from the fd (when there might be more io
+ *            queued -- the receive buffer might still be non-empty)
+ *
+ */
+static int handle_fifo_read(struct ctrl_socket* cs, int idx)
+{
+	int bytes_free;
+	int bytes_read;
+	int bytes_needed;
+	int bytes_processed;
+	struct stream_req* r;
+	struct send_handle sh;
+	struct stream_connection* sc;
+	
+	sh.fd=-1;
+	sh.type=S_FIFO;
+	sh.from_len=0;
+	sc=(struct stream_connection *)cs->data;
+	r=&(sc->req);
+	bytes_free=STREAM_BUF_SIZE-(int)(r->end-r->buf);
+	if (bytes_free==0){
+		LOG(L_ERR, "ERROR: handle_fifo_read: buffer overrun\n");
+		goto error;
+	}
+again:
+	bytes_read=read(cs->fd, r->end, bytes_free);
+	if (bytes_read==-1){
+		if ((errno==EAGAIN)||(errno==EWOULDBLOCK)){
+			goto no_read; /* nothing has been read */
+		}else if (errno==EINTR) goto again;
+		LOG(L_ERR, "ERROR: handle_fifo_read: error reading: %s [%d]\n",
+				strerror(errno), errno);
+		goto error_read;
+	}else if(bytes_read==0){ /* eof */
+		DEBUG("handle_fifo_read: eof on %s\n", cs->name);
+	}
+	r->end+=bytes_read;
+	if (bytes_read && (bytes_read<r->bytes_to_go)){
+		r->bytes_to_go-=bytes_read;
+		goto skip; /* not enough bytes read, no point in trying to process
+					  them */
+	}
+	do{
+		bytes_processed=fifo_process((char*)r->proc, (int)(r->end-r->proc),
+										&bytes_needed, &sh, &sc->saved_state);
+		if (bytes_processed<0){
+			/* error while processing the packet => skip */
+			goto discard;
+		}
+		r->proc+=bytes_processed;
+		r->bytes_to_go=bytes_needed;
+		if (bytes_needed>0){
+			if (bytes_read==0){ /*want more bytes, but we have eof*/
+				LOG(L_ERR, "ERROR: handle_fifo_read: unexpected EOF\n");
+				goto discard; /* discard buffered contents */
+			}
+			break; /* no more read bytes ready for processing */
+		}
+		/* if (bytes_needed==0) -- packet fully processed */
+		sc->saved_state=0; /* reset per datagram state */
+		if (bytes_processed==0){
+			/* nothing processed, nothing needed, no error - looks like
+			 * a bug */
+			LOG(L_ERR, "ERROR: handle_fifo_read: unexpected return\n");
+			goto discard;
+		}
+	}while(r->proc<r->end);
+	/* free some space in the buffer */
+	if (r->proc>r->buf){
+		if (r->end>r->proc){
+			memmove(r->buf, r->proc, (int)(r->end-r->proc));
+			r->end-=(int)(r->proc-r->buf);
+		}else{
+			r->end=r->buf;
+		}
+		r->proc=r->buf;
+	}
+skip:
+	/* everything went fine, we just have to read more */
+	return 1;
+error_read:
+	/* temporary read error ? */
+	r->proc=r->buf;
+	r->end=r->buf;
+	return -1;
+discard:
+	/* reset the whole receive buffer */
+	r->proc=r->buf;
+	r->end=r->buf;
+	sc->saved_state=0; /* reset saved state */
+	return 1;
+no_read:
+	/* false alarm */
+	return 0;
+error:
+	return 1; /* there's nothing wrong with the fifo, just a bad  application
+				 packet */
+	/* close connection */
+}
+#endif
+
+static int handle_fd_pass(struct ctrl_socket* cs)
+{
+	char rcvbuff[MAX_IO_READ_CONNECTIONS];
+	ssize_t rcvd;
+	int i, new_sock;
+	struct ctrl_socket *rcvd_cs;
+	union sockaddr_u from;
+	socklen_t from_len;
+
+	if (cs->child != 0) { /* binrpc connection listener */
+		do {
+			rcvd = read(cs->fd, rcvbuff, sizeof(rcvbuff));
+			if (0 < rcvd) {
+				DEBUG("binrpc child PID#%d released %zd connection(s).\n",
+						cs->child, rcvd);
+				for (i = 0; i < rcvd; i ++)
+					pt_update_load(pt_worker(cs->child), /*decrease load*/-1);
+			} else if (rcvd < 0) {
+				if (errno == EINTR) {
+					continue;
+				} else {
+					ERR("failed to read binrpc child data: %s [%d].\n",
+							strerror(errno), errno);
+					goto fail;
+				}
+			} else { /* reset */
+				ERR("binrpc worker disconnected!\n");
+				goto fail;
+			}
+			break;
+		} while (1);
+	} else { /* binrpc worker */
+		new_sock = -1;
+		if (receive_fd(cs->fd, &rcvd_cs, sizeof(rcvd_cs/*ptr!*/), &new_sock, 
+				MSG_WAITALL) < sizeof(rcvd_cs)) {
+			ERR("failed to receive connection descriptor: %s [%d].\n", 
+					strerror(errno), errno);
+			goto fail;
+		}
+		from_len = rcvd_cs->addr.addrlen; /* can only be same address length */
+		if (getpeername(new_sock, &from.sa_in.s, &from_len) < 0) {
+			WARN("failed to get peer's address from passed connection"
+					" descriptor: %s [%d].\n", strerror(errno), errno);
+			/* TODO: check if fatal */
+		}
+		if (add_stream_connection(new_sock, rcvd_cs, &from) < 0)
+			goto close;
+	}
+	return 0;
+fail:
+	/* fail safe */
+	exit(-1);
+close:
+	if (0 <= new_sock)
+		close(new_sock);
+	return 1;
+}
+
+/* generic handle io routine, it will call the appropiate
+ *  handle_xxx() based on the fd_map type
+ *
+ * params:  fm  - pointer to a fd hash entry
+ *          idx - index in the fd_array (or -1 if not known)
+ * return: -1 on error
+ *          0 on EAGAIN or when by some other way it is known that no more 
+ *            io events are queued on the fd (the receive buffer is empty).
+ *            Usefull to detect when there are no more io events queued for
+ *            sigio_rt, epoll_et, kqueue.
+ *         >0 on successfull read from the fd (when there might be more io
+ *            queued -- the receive buffer might still be non-empty)
+ */
+inline static int handle_io(struct fd_map* fm, short events, int idx)
+{
+	int ret;
+	
+	switch(fm->type){
+		case F_T_FDPASS:
+			ret = handle_fd_pass((struct ctrl_socket*)fm->data);
+			break;
+		case F_T_CTRL_DGRAM:
+#if 0
+			ret=handle_ctrl_dgram((struct ctrl_socket*)fm->data);
+#else
+			ret=handle_dgram((struct ctrl_socket*)fm->data);
+#endif
+			break;
+		case F_T_CTRL_STREAM:
+			ret=handle_new_connect((struct ctrl_socket*)fm->data);
+			break;
+		case F_T_READ_STREAM:
+#if 0
+			ret=handle_stream_read((struct stream_connection*)fm->data, idx);
+#else
+			ret=handle_read((struct stream_connection*)fm->data, idx);
+#endif
+			break;
+#ifdef USE_FIFO
+		case F_T_FIFO:
+			ret=handle_fifo_read((struct ctrl_socket*)fm->data, idx);
+			break;
+#endif
+		case F_T_RESERVED:
+			LOG(L_CRIT, "BUG: io listen handle_io: emtpy fd map\n");
+			goto error;
+		default:
+			LOG(L_CRIT, "BUG: io listen handle_io: unknown fd type %d\n",
+					fm->type);
+			goto error;
+	}
+	return ret;
+error:
+	return -1;
+}
+
+
+
+void io_listen_who_rpc(rpc_t* rpc, void* ctx)
+{
+	struct stream_connection* sc;
+	union sockaddr_union *sa_in;
+	struct ip_addr ip;
+	int port;
+	int i;
+	
+	i=0;
+	/* check if called from another process */
+	if (stream_conn_lst.next==0){
+		rpc->fault(ctx, 606, "rpc available only over binrpc (ctl)");
+		return;
+	}
+	/* p_proto transport from sport to tport*/
+	clist_foreach(&stream_conn_lst, sc, next){
+		i++;
+		rpc->add(ctx, "ss", payload_proto_name(sc->parent->p_proto),
+								socket_proto_name(&sc->parent->addr));
+		switch (sc->parent->addr.domain) {
+			case PF_INET:
+			case PF_INET6:
+				su2ip_addr(&ip, &sc->from.sa_in);
+				port=su_getport(&sc->from.sa_in);
+				rpc->add(ctx, "ss", ip_addr2a(&ip), int2str(port, 0));
+				sa_in = (union sockaddr_union *)&sc->parent->addr.sockaddr;
+				su2ip_addr(&ip, sa_in);
+				port=su_getport(sa_in);
+				rpc->add(ctx, "ss", ip_addr2a(&ip), int2str(port, 0));
+				break;
+			case PF_LOCAL:
+				/* TODO: is this buggy in ctl? */
+				rpc->add(ctx, "ss", "<anonymous unix socket>", "" );
+				break;
+#ifdef USE_FIFO
+			case PF_FIFO:
+#endif		
+				/* TODO: just use CTRLSOCK_URI */
+				rpc->add(ctx, "ss", sc->parent->name, "");
+				break;
+			default:
+				BUG("invalid socket domain %d.\n", sc->parent->addr.domain);
+				rpc->add(ctx, "ssss", "<bug unknown protocol>",
+								"", "", "", "");
+		}
+		
+		/* idle time
+		 * rpc->add(ctx, "d", TICKS_TO_MS(get_ticks_raw()-
+								(sc->expire-IO_STREAM_CONN_TIMEOUT)));*/
+	}
+	if (i==0){
+		rpc->fault(ctx, 400, "no open stream connection");
+	}
+}
+
+
+
+void io_listen_conn_rpc(rpc_t* rpc, void* ctx)
+{
+	/* check if called from another process */
+	if (stream_conn_lst.next==0){
+		rpc->fault(ctx, 606, "rpc available only over binrpc (ctl)");
+		return;
+	}
+	rpc->add(ctx, "d", io_read_connections);
+}

+ 64 - 0
modules/binrpc/io_listener.h

@@ -0,0 +1,64 @@
+/*
+ * $Id: io_listener.h,v 1.1 2006/02/23 19:57:31 andrei Exp $
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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:
+ *    [email protected]
+ *
+ * ser 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
+ */
+/* History:
+ * --------
+ *  2006-02-15  created by andrei
+ *  2007        ported to libbinrpc (bpintea)
+ */
+
+#ifndef _io_listener_h
+#define _io_listener_h
+#include "ctrl_socks.h"
+
+enum sock_con_type { S_CONNECTED, S_DISCONNECTED
+#ifdef USE_FIFO
+						, S_FIFO
+#endif
+};
+
+union sockaddr_u{
+	union sockaddr_union sa_in;
+	struct sockaddr_un sa_un;
+};
+
+
+struct send_handle{
+	int fd;
+	int type;
+	union sockaddr_u from;
+	unsigned int from_len;
+};
+
+
+
+int sock_send_v(void *h, struct iovec* v, size_t count);
+
+void io_listen_loop(struct ctrl_socket* cs_lst);
+
+extern int have_connection_listener;
+
+#endif

+ 573 - 0
modules/binrpc/libbinrpc_wrapper.c

@@ -0,0 +1,573 @@
+/*
+ * Copyright (c) 2010 IPTEGO GmbH.
+ * All rights reserved.
+ *
+ * This file is part of sip-router, a free SIP server.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Bogdan Pintea.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../rpc.h"
+#include "../../str.h"
+#include "../../sr_module.h"
+
+#include "libbinrpc_wrapper.h"
+
+#define TX_TIMEOUT				10000
+#define MAX_RPC_FAULT_REASON	256
+#define MAX_RPC_PRINTF_BUF_LEN	256
+
+#define CALL_ID_DESC	"(%s()#%d)"
+#define CALL_ID_VAL(_req_)	brpc_method(_req_)->val, brpc_id(_req_)
+
+typedef struct {
+	brpc_t *req;
+	brpc_dissect_t *diss; /* list of dissectors */
+	struct reply_to sndto;
+	brpc_t *rpl;
+	bool sent; /* callback invoked rpc_send() */
+	bool noreply; /* if true, callback did not add any value */
+	enum BRPC_RUN_RET status; /* status of RPC call */
+} rpc_ctx_t;
+
+
+static int rpc_send(rpc_ctx_t* ctx);
+static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...);
+static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...);
+static int rpc_scan(rpc_ctx_t *ctx, char* fmt, ...);
+static int rpc_printf(rpc_ctx_t* ctx, char* fmt, ...);
+static int rpc_struct_add(brpc_val_t* ctx, char* fmt, ...);
+static int rpc_struct_scan(brpc_dissect_t *ctx, char* fmt, ...);
+static int rpc_struct_printf(brpc_val_t* ctx, char* name, char* fmt, ...);
+
+static rpc_t rpc_callbacks = {
+	(rpc_fault_f)			rpc_fault,
+	(rpc_send_f)			rpc_send,
+	(rpc_add_f)				rpc_add,
+	(rpc_scan_f)			rpc_scan,
+	(rpc_printf_f)			rpc_printf,
+	(rpc_struct_add_f)		rpc_struct_add,
+	(rpc_struct_scan_f)		rpc_struct_scan,
+	(rpc_struct_printf_f)	rpc_struct_printf
+};
+
+/* should a 200 reply be sent back if callback didn't do it? */
+int force_reply = 1; 
+
+static rpc_ctx_t *new_rpc_ctx(brpc_t *req, struct reply_to sndto)
+{
+	rpc_ctx_t *ctx;
+	ctx = (rpc_ctx_t *)pkg_malloc(sizeof(rpc_ctx_t));
+	if (! ctx) {
+		ERR("out of pkg memory.\n");
+		return NULL;
+	}
+	ctx->req = req;
+	ctx->diss = NULL;
+	ctx->sndto = sndto;
+	
+	ctx->rpl = brpc_rpl(req);
+	if (! ctx->rpl) {
+		ERR("failed to build reply framework for req %u: %s [%d].\n", 
+				brpc_id(req), brpc_strerror(), brpc_errno);
+		goto error;
+	}
+
+	ctx->sent = false;
+	ctx->noreply = true;
+	ctx->status = BRPC_RUN_UNKNOWN;
+	return ctx;
+error:
+	pkg_free(ctx);
+	return NULL;
+}
+
+void rpc_ctx_free(rpc_ctx_t *ctx)
+{
+	if (ctx->req)
+		brpc_finish(ctx->req);
+	if (ctx->rpl)
+		brpc_finish(ctx->rpl);
+	if (ctx->diss)
+		brpc_dissect_free(ctx->diss);
+	pkg_free(ctx);
+}
+
+
+enum BRPC_RUN_RET binrpc_run(brpc_t *req, struct reply_to sndto)
+{
+	const brpc_str_t *mname;
+	rpc_export_t *rpc_export;
+	rpc_ctx_t *ctx = NULL;
+	enum BRPC_RUN_RET status;
+
+	if (brpc_type(req) != BRPC_CALL_REQUEST) {
+		ERR("received non request BINRPC call.\n");
+		return BRPC_RUN_VOID;
+	}
+
+	mname = brpc_method(req);
+	if (! mname) {
+		ERR("failed to extract function call name: %s [%d].\n", 
+				brpc_strerror(), brpc_errno);
+		return BRPC_RUN_VOID;
+	}
+
+	ctx = new_rpc_ctx(req, sndto);
+	if (! ctx) {
+		ERR("failed go get a RPC requst context " CALL_ID_DESC ".\n",
+				CALL_ID_VAL(req));
+		return BRPC_RUN_VOID;
+	}
+
+	rpc_export = find_rpc_export(mname->val, /*flags*/0);
+	if (! rpc_export) {
+		ERR("no callback found for RPC request " CALL_ID_DESC ".\n", 
+				CALL_ID_VAL(req));
+		rpc_fault(ctx, BRPC_EMETH, "No such call (`%s').", mname->val);
+		goto send; 
+	} else {
+		DEBUG("dispatching RPC request " CALL_ID_DESC ".\n", 
+				CALL_ID_VAL(req));
+		rpc_export->function(&rpc_callbacks, ctx);
+	}
+	
+	if (! ctx->sent) {
+		if (ctx->noreply) {
+			if (force_reply)
+				rpc_add(ctx, "ds", 200, "OK (auto).");
+			else
+				ctx->sent = true;
+		}
+send:
+		rpc_send(ctx);
+	}
+
+	status = ctx->status;
+	rpc_ctx_free(ctx);
+	return status;
+}
+
+static int rpc_send(rpc_ctx_t* ctx)
+{
+	if (ctx->sent) {
+		DEBUG("reply already sent.\n");
+		return 0;
+	}
+	ctx->sent = true; /* prevent future attempts */
+
+	if (! brpc_sendto(ctx->sndto.fd, /*destination*/&ctx->sndto.addr, 
+			ctx->rpl, TX_TIMEOUT)) {
+		ERR("error occured while sending reply " CALL_ID_DESC ".\n" ,
+				CALL_ID_VAL(ctx->req));
+		switch (brpc_errno) {
+			case EINPROGRESS:
+				ERR("...due to timing out while sending.\n");
+				ctx->status = BRPC_RUN_ABORT;
+				break;
+			case EMSGSIZE:
+				ERR("...due to reply message size.\n");
+				/* TODO: send fault */
+				ctx->status = BRPC_RUN_VOID;
+				break;
+			case ETIMEDOUT:
+				ERR("...due to write timeout.\n");
+				ctx->status = BRPC_RUN_VOID;
+			default:
+				ERR("...due to: %s [%d].\n", brpc_strerror(), brpc_errno);
+				ctx->status = BRPC_RUN_VOID;
+		}
+	} else {
+		DEBUG("call " CALL_ID_DESC " successfully replied.\n", 
+				CALL_ID_VAL(ctx->req));
+		ctx->status = BRPC_RUN_SUCCESS;
+	}
+
+	return (ctx->status == BRPC_RUN_SUCCESS) ? 0 : -1;
+}
+
+static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...)
+{
+	va_list ap;
+	char buff[MAX_RPC_FAULT_REASON];
+	brpc_str_t reason = {buff, sizeof(buff)};
+
+	if (ctx->sent) {
+		BUG("trying to return fault, but a reply already dispatched "
+				CALL_ID_DESC ".\n", CALL_ID_VAL(ctx->req));
+		return;
+	}
+	if (brpc_is_fault(ctx->rpl)) {
+		WARN("reply already set to fault.");
+		return;
+	}
+
+	va_start(ap, fmt);
+	reason.len = vsnprintf(buff, sizeof(buff), fmt, ap);
+	va_end(ap);
+
+	/* throw away all values that might have been added */
+	if (! ctx->noreply) {
+		brpc_finish(ctx->rpl);
+		ctx->rpl = brpc_rpl(ctx->req);
+		if (! ctx->rpl) {
+			ERR("failed to (re)build failure framework " CALL_ID_DESC 
+					": %s [%d].\n", CALL_ID_VAL(ctx->req),
+					brpc_strerror(), brpc_errno);
+			ctx->status = BRPC_RUN_VOID;
+			return;
+		}
+	}
+	ctx->noreply = false;
+
+	if (! brpc_fault(ctx->rpl, (brpc_int_t *)&code, &reason)) {
+		ERR("failed to signal request failure: %s [%d].\n", brpc_strerror(),
+				brpc_errno);
+		ctx->status = BRPC_RUN_VOID;
+	}
+}
+
+static inline brpc_val_t *new_brpc_val(char desc, va_list *ap)
+{
+	brpc_int_t fp; /*floating point*/
+	char *reason_c;
+	str *reason_s;
+	brpc_val_t *_val;
+
+	switch (desc) {
+		case 't':
+		case 'b':
+		case 'd':
+			_val = brpc_int(va_arg(*ap, brpc_int_t));
+			break;
+		case 'f':
+			/* TODO: support me */
+			fp = (brpc_int_t)va_arg(*ap, double);
+			WARN("floating point not supported, yet; faking as "
+					"int=%d.\n", fp);
+			_val = brpc_int(fp);
+			break;
+		case 's':
+			reason_c = va_arg(*ap, char *);
+			if (reason_c)
+				_val = brpc_str(reason_c, strlen(reason_c)+/*0-term*/1);
+			else
+				_val = brpc_null(BRPC_VAL_STR);
+			break;
+		case 'S':
+			reason_s = va_arg(*ap, str *);
+			if (reason_s)
+				_val = brpc_str(reason_s->s, reason_s->len);
+			else
+				_val = brpc_null(BRPC_VAL_STR);
+			break;
+		case '{':
+			_val = brpc_map(NULL);
+			*va_arg(*ap, brpc_val_t **) = _val;
+			break;
+		/*TODO: '[', '('*/
+		default:
+			ERR("invalid descriptor `%c'.\n", desc);
+			return NULL;
+	}
+	return _val;
+}
+
+static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
+{
+	brpc_val_t *val;
+	va_list ap;
+	char desc;
+
+	if (brpc_is_fault(ctx->rpl)) {
+		ERR("can not add values anymore: reply already set to fault.");
+		return -1;
+	}
+
+	ctx->noreply = false;
+
+	va_start(ap, fmt);
+	while ((desc = *fmt)) {
+		if (! (val = new_brpc_val(desc, &ap))) {
+			ERR("failed to build BINRPC value; (descriptor: %s): %s [%d]", 
+					fmt, brpc_strerror(), brpc_errno);
+			goto error;
+		}
+		fmt ++;
+		if (! brpc_add_val(ctx->rpl, val)) {
+			ERR("failed to add BINRPC value to reply: %s [%d].\n", 
+					brpc_strerror(), brpc_errno);
+			brpc_val_free(val);
+			return -1;
+		}
+	}
+	va_end(ap);
+
+	return 0;
+error:
+	rpc_fault(ctx, 500, "Failed to build response.");
+	return -1;
+}
+
+static int rpc_struct_add(brpc_val_t *map, char* fmt, ...)
+{
+	brpc_val_t *avp_name = NULL, *avp_val, *avp /*4 GCC*/= NULL;
+	va_list ap;
+	char desc;
+	char *member_name;
+
+	va_start(ap, fmt);
+	while ((desc = *fmt)) {
+		member_name = va_arg(ap, char *);
+		avp_name = brpc_cstr(member_name);
+		if (! avp_name) {
+			fprintf(stderr, "ERROR: failed to build structure member name"
+					" value: %s [%d].\n", brpc_strerror(), brpc_errno);
+			return -1;
+		}
+
+		if (! (avp_val = new_brpc_val(desc, &ap))) {
+			ERR("failed to build BINRPC value; (descriptor: %s): %s [%d]", 
+					fmt, brpc_strerror(), brpc_errno);
+			goto error;
+		}
+		fmt ++;
+	
+		if (! (avp = brpc_avp(avp_name, avp_val))) {
+			fprintf(stderr, "ERROR: failed to build BINRPC AVP: %s [%d].\n",
+					brpc_strerror(), brpc_errno);
+			goto error;
+		}
+	
+		if (! brpc_map_add(map, avp)) {
+			ERR("failed to add BINRPC AVP to MAP: %s [%d].\n", 
+					brpc_strerror(), brpc_errno);
+			goto error;
+		}
+	}
+	va_end(ap);
+
+	return 0;
+error:
+	if (avp) {
+		brpc_val_free(avp);
+	} else {
+		if (avp_name)
+			brpc_val_free(avp_name);
+		if (avp_val)
+			brpc_val_free(avp_val);
+	}
+	return -1;
+}
+
+static inline const brpc_val_t *extract_val(brpc_dissect_t *diss, 
+		brpc_vtype_t expected_type)
+{
+	const brpc_val_t *val;
+	if (! brpc_dissect_next(diss)) {
+		ERR("scanned value not present in received message, at this point.\n");
+		return NULL;
+	}
+	val = brpc_dissect_fetch(diss);
+	if (brpc_val_type(val) != expected_type) {
+		ERR("scanned value [%d] not of expected type in received "
+				"message at this point [%d].\n", 
+				expected_type, brpc_val_type(val));
+		return NULL;
+	}
+	return val;
+}
+
+int brpc_scan(brpc_dissect_t *_diss, char *_fmt, va_list ap)
+{
+	;
+	const brpc_val_t *val;
+	str *string;
+	int scanned;
+	brpc_dissect_t *diss_ctx;
+	
+	scanned = 0;
+	while (*_fmt) {
+		switch (*_fmt ++) {
+			/* TODO: support for structs scanning */
+			case 't':
+			case 'b':
+			case 'd':
+			case 'f': /* TODO: fixme */
+				if(! (val = extract_val(_diss, BRPC_VAL_INT)))
+					return -1;
+				if (brpc_is_null(val)) {
+					ERR("SER can't handle yet(?) null numeric values.\n");\
+					return -1;
+				}
+				*va_arg(ap, int *) = brpc_int_val(val);
+				break;
+			case 'S':
+				if(! (val = extract_val(_diss, BRPC_VAL_STR)))
+					return -1;
+				string = va_arg(ap, str *);
+				if (brpc_is_null(val)) {
+					ERR("SER can't handle properly (for now?) null str "
+							"values; simulating by zero-len str.\n");
+					string->s = NULL;
+					string->len = 0;
+				} else {
+					string->s = brpc_str_val(val).val;
+					string->len = brpc_str_val(val).len;
+				}
+				break;
+			case 's':
+				if(! (val = extract_val(_diss, BRPC_VAL_STR)))
+					return -1;
+				*va_arg(ap, char **) = brpc_str_val(val).val;
+				break;
+			case '{':
+				if (! (val = extract_val(_diss, BRPC_VAL_MAP)))
+					return -1;
+				if (! (diss_ctx = brpc_val_dissector((brpc_val_t *)val))){\
+					ERR("failed to get BINRPC value dissector: "
+							"%s [%d].\n", brpc_strerror(), brpc_errno);
+					return -1;
+				}
+				if (! brpc_dissect_chain(_diss, diss_ctx)) {
+					ERR("failed to anchor BINRPC dissector: %s [%d].\n",
+							brpc_strerror(), brpc_errno);
+					brpc_dissect_free(diss_ctx);
+					return -1;
+				}
+				*va_arg(ap, brpc_dissect_t **) = diss_ctx;
+				break;
+			/* TODO: '[', '(' */
+			default:
+				ERR("unsupported scan descriptor `%c'(0x%x)", 
+						_fmt[-1], _fmt[-1]);
+				return -1;
+		}
+		scanned ++;
+	}
+	va_end(ap);
+	return scanned;
+}
+
+static int rpc_scan(rpc_ctx_t *ctx, char *fmt, ...)
+{
+	brpc_dissect_t *diss;
+	va_list ap;
+	int scanned;
+
+	if ((! ctx->diss) && (! (ctx->diss = brpc_msg_dissector(ctx->req)))) {
+		ERR("failed to get BINRPC message dissector: %s [%d].\n", 
+				brpc_strerror(), brpc_errno);
+		return -1;
+	}
+	diss = ctx->diss;
+
+	va_start(ap, fmt);
+	scanned = brpc_scan(diss, fmt, ap);
+	va_end(ap);
+	return scanned;
+}
+
+static int rpc_struct_scan(brpc_dissect_t *ctx, char* fmt, ...)
+{
+	int scanned;
+	va_list ap;
+
+	va_start(ap, fmt);
+	scanned = brpc_scan(ctx, fmt, ap);
+	va_end(ap);
+	return scanned;
+}
+
+
+#define get_string(_fmt, _val) \
+	do { \
+		char buff[MAX_RPC_PRINTF_BUF_LEN]; \
+		va_list ap; \
+		int len; \
+		\
+		va_start(ap, fmt); \
+		len = vsnprintf(buff, MAX_RPC_PRINTF_BUF_LEN, fmt, ap); \
+		va_end(ap); \
+		if (len < 0) { \
+			ERR("failed to print: %s [%d].\n", strerror(errno), errno); \
+			return -1; \
+		} \
+		if (! (_val = brpc_str(buff, len))) { \
+			ERR("failed to build BINRPC string value: %s [%d].\n", \
+					brpc_strerror(), brpc_errno); \
+			return -1; \
+		} \
+	} while (0)
+
+static int rpc_printf(rpc_ctx_t* ctx, char* fmt, ...)
+{
+	brpc_val_t *val;
+	
+	get_string(fmt, val);
+	if (! brpc_add_val(ctx->rpl, val)) {
+		ERR("failed to add BINRPC string value to reply: %s [%d].\n",
+				brpc_strerror(), brpc_errno);
+		brpc_val_free(val);
+		return -1;
+	}
+
+	return 1;
+}
+
+
+static int rpc_struct_printf(brpc_val_t* ctx, char* name, char* fmt, ...)
+{
+	brpc_val_t *val;
+	
+	switch (ctx->type) {
+		case BRPC_VAL_MAP:
+			break;
+		case BRPC_VAL_LIST:
+		case BRPC_VAL_AVP:
+			ERR("sequence value of type %d not yet (?) supported for this RPC "
+					"API call.\n", ctx->type);
+			return -1;
+		default:
+			BUG("illegal value as of type sequence: %d.\n", ctx->type);
+			return -1;
+	}
+
+	get_string(fmt, val);
+	if (! (brpc_map_add(ctx, val))) {
+		ERR("failed to add BINRPC value to MAP: %s [%d].\n", brpc_strerror(),
+				brpc_errno);
+		brpc_val_free(val);
+		return -1;
+	}
+	return 1;
+}
+

+ 58 - 0
modules/binrpc/libbinrpc_wrapper.h

@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010 IPTEGO GmbH.
+ * All rights reserved.
+ *
+ * This file is part of sip-router, a free SIP server.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Bogdan Pintea.
+ */
+
+
+#ifndef __LIBBINRPC_WRAPPER_H__
+#define __LIBBINRPC_WRAPPER_H__
+
+#include <binrpc.h>
+
+enum BRPC_RUN_RET {
+	/* send broken: reset connection */
+	BRPC_RUN_ABORT = -2,
+	/* nothing could be sent: keep conn */
+	BRPC_RUN_VOID,
+	/* BUG (init value) */
+	BRPC_RUN_UNKNOWN,
+	/* all OK */
+	BRPC_RUN_SUCCESS,
+};
+
+struct reply_to {
+	int fd; /* where to write */
+	brpc_addr_t addr; /* where to send */
+};
+
+enum BRPC_RUN_RET binrpc_run(brpc_t *req, struct reply_to sndto);
+
+extern int force_reply; 
+
+#endif /* __LIBBINRPC_WRAPPER_H__ */

+ 38 - 0
modules/binrpc/modbrpc_defaults.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010 IPTEGO GmbH.
+ * All rights reserved.
+ *
+ * This file is part of sip-router, a free SIP server.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Bogdan Pintea.
+ */
+
+#ifndef __MODBRPC_DEFAULTS_H__
+#define __MODBRPC_DEFAULTS_H__
+
+/*listen by default on: */
+#define DEFAULT_MODBRPC_SOCKET  "brpcld:/tmp/ser_binrpc.usock"
+
+#endif /* __MODBRPC_DEFAULTS_H__ */

+ 160 - 0
modules/binrpc/proctab.c

@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2010 IPTEGO GmbH.
+ * All rights reserved.
+ *
+ * This file is part of sip-router, a free SIP server.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Bogdan Pintea.
+ */
+
+#ifdef EXTRA_DEBUG
+#include <assert.h>
+#include <stdlib.h>
+#endif
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "proctab.h"
+
+struct pt_entry {
+	pid_t pid;
+	union {
+		int fd;
+		struct ctrl_socket *cs;
+	};
+	size_t conns;
+};
+
+static struct pt_entry *pt;
+static size_t pt_size = 0;
+
+
+int pt_init(size_t count)
+{
+	if (! (pt = pkg_malloc(count * sizeof(struct pt_entry)))) {
+		ERR("out of pkg memory.\n");
+		return -1;
+	}
+	pt_size = count;
+	memset(pt, 0, pt_size * sizeof(struct pt_entry));
+	return 0;
+}
+
+int pt_ins(pid_t pid, int fd)
+{
+	static size_t idx = 0;
+
+#ifdef EXTRA_DEBUG
+	assert(idx < pt_size);
+#endif
+	DEBUG("PT insertion @#%zd, PID: %d, fd=%d.\n", idx, pid, fd);
+
+	pt[idx].pid = pid;
+	pt[idx].fd = fd;
+	idx ++;
+	return 0;
+}
+
+ssize_t pt_least_loaded(size_t *load)
+{
+	int i, choice = -1;
+	size_t min;
+
+	for (i = 0, min = INT_MAX; i < pt_size; i ++)
+		if (pt[i].conns < min) {
+			min = pt[i].conns;
+			choice = i;
+		}
+#ifdef EXTRA_DEBUG
+	assert(min < INT_MAX);
+	assert(0 <= choice);
+#else
+	BUG("no worker available for new connection!\n");
+#endif
+	*load = min;
+	DEBUG("least loaded @#%d, with %zd connections.\n", choice, min);
+	return choice;
+}
+
+struct ctrl_socket *pt_update_load(ssize_t worker, int direction)
+{
+#ifdef EXTRA_DEBUG
+	assert(worker < pt_size);
+#endif
+	if (direction < 0)
+		pt[worker].conns --;
+	else
+		pt[worker].conns ++;
+	DEBUG("binrpc worker PID#%d loaded with %zd connection(s) "
+			"(after %s).\n", pt[worker].pid, pt[worker].conns, 
+			(direction < 0) ? "unloading" : "loading");
+	return pt[worker].cs;
+}
+
+pid_t pt_pid(ssize_t worker)
+{
+#ifdef EXTRA_DEBUG
+	assert(worker < pt_size);
+#endif
+	return pt[worker].pid;
+}
+
+ssize_t pt_worker(pid_t child)
+{
+	ssize_t worker = -1, i;
+	for (i = 0; i < pt_size; i ++)
+		if (pt[i].pid == child)
+			return i;
+	BUG("worker index for PID#%d not found.\n", child);
+#ifdef EXTRA_DEBUG
+	abort();
+#endif
+	return worker;
+}
+
+int pt_fd2cs(void)
+{
+	int i;
+	struct ctrl_socket *cs;
+
+	for (i = 0; i < pt_size; i ++) {
+		if (! (cs = add_fdpass_socket(pt[i].fd, pt[i].pid))) {
+			ERR("failed to turn sock descriptor into control struct.\n");
+			return -1;
+		} else {
+			pt[i].cs = cs;
+		}
+	}
+	return 0;
+}
+
+void pt_free(int has_cs)
+{
+	int i;
+
+	if (! has_cs)
+		for (i = 0; i < pt_size; i ++)
+			close(pt[i].fd);
+	pkg_free(pt);
+}

+ 48 - 0
modules/binrpc/proctab.h

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 IPTEGO GmbH.
+ * All rights reserved.
+ *
+ * This file is part of sip-router, a free SIP server.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Bogdan Pintea.
+ */
+
+#ifndef __BINRPC_PT_H__
+#define __BINRPC_PT_H__
+
+#include <unistd.h>
+#include "ctrl_socks.h"
+
+int pt_init(size_t count);
+int pt_ins(pid_t pid, int fd);
+ssize_t pt_least_loaded(size_t *load);
+struct ctrl_socket *pt_update_load(ssize_t worker, int direction);
+pid_t pt_pid(ssize_t worker);
+ssize_t pt_worker(pid_t child);
+int pt_fd2cs(void);
+void pt_free(int has_cs);
+
+
+#endif /* __BINRPC_PT_H__ */