Browse Source

app_lua: run Lua scripts from cfg

- new module to run embedded Lua scripts from cfg
- module exports functions to Lua in order to access current processed
  SIP message
- uses two Lua contexts, one for cached scripts (run functions from them
  at runtime) and one for execution of entire Lua scripts at runtime
- so far exported to Lua
	- dbg, err and log functions - print messages using internal DBG API
	- sr.hdr.{append, insert, remove, append_to_reply} - header
	  management
	- sr.pv.{get, seti, setf, unset, is_null} - PV management
Daniel-Constantin Mierla 15 years ago
parent
commit
7b91c4d48e

+ 18 - 0
modules/app_lua/Makefile

@@ -0,0 +1,18 @@
+# $Id: Makefile 2 2005-06-13 16:47:24Z bogdan_iancu $
+#
+# print example module makefile
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=app_lua.so
+LIBS= -llua5.1
+
+DEFS+=-DOPENSER_MOD_INTERFACE -I/usr/include/lua5.1/
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+
+include ../../Makefile.modules

+ 186 - 0
modules/app_lua/README

@@ -0,0 +1,186 @@
+app_lua Module
+
+Daniel-Constantin Mierla
+
+   asipto.com
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2010 Daniel-Constantin Mierla (asipto.com)
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Exported Parameters
+
+              3.1. load (string)
+
+        4. Exported Functions
+
+              4.1. lua_dofile(path)
+              4.2. lua_dostring(script)
+              4.3. lua_run(function, params)
+
+        5. Example of usage
+
+   List of Examples
+
+   1.1. Set load parameter
+   1.2. lua_dofile usage
+   1.3. lua_doscript usage
+   1.4. lua_doscript usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. load (string)
+
+   4. Exported Functions
+
+        4.1. lua_dofile(path)
+        4.2. lua_dostring(script)
+        4.3. lua_run(function, params)
+
+   5. Example of usage
+
+1. Overview
+
+   This module allows executing Lua scripts from config file. It exports a
+   set of functions to Lua in order to access the current processed SIP
+   message. These functions are within Lua module 'sr'.
+
+   Lua (http://www.lua.org) is a fast and easy to embed scripting
+   language. Exported API from SIP router to Lua is documented in the
+   dokuwiki.
+
+   The module has two Lua contexts:
+     * first is used for functions lua_dofile() and lua_dostring().
+     * second is used for function lua_run() and parameter 'load'.
+       Therefore lua_run() cannot execute functions from scripts loaded
+       via lua_dofile() in config. This is kind of caching mode, avoiding
+       reading file every time, but you must be sure you do not have
+       someting that is executed by default and requires access to SIP
+       message.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * none.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * liblua5.1-dev - Lua devel library.
+
+3. Exported Parameters
+
+   3.1. load (string)
+
+3.1. load (string)
+
+   Set the path to the Lua script to be loaded at startup. Then you can
+   use lua_run(function, params) to execute a function from the script at
+   runtime.
+
+   Default value is "null".
+
+   Example 1.1. Set load parameter
+...
+modparam("lua", "lua", "/usr/local/etc/kamailio/lua/myscript.lua")
+...
+
+4. Exported Functions
+
+   4.1. lua_dofile(path)
+   4.2. lua_dostring(script)
+   4.3. lua_run(function, params)
+
+4.1.  lua_dofile(path)
+
+   Execute the Lua script stored in 'path'.
+
+   Example 1.2. lua_dofile usage
+...
+lua_dofile("/usr/local/etc/kamailio/lua/myscript.lua");
+...
+
+4.2.  lua_dostring(script)
+
+   Execute the Lua script stored in parameter.
+
+   Example 1.3. lua_doscript usage
+...
+if(!lua_dostring("sr.log([[err]], [[----------- Hello World\n]])"))
+{
+    xdbg("SCRIPT: failed to execute lua script!\n");
+}
+...
+
+4.3.  lua_run(function, params)
+
+   Execute the Lua function 'func' giving params as parameters. There can
+   be up to 3 string parameters. The function must exist in the script
+   loaded at startup via parameter 'load'. So far the parameters are
+   considered static strings (allowing pv is in the roadmap).
+
+   Example 1.4. lua_doscript usage
+...
+if(!lua_run("sr_append_fu_to_reply"))
+{
+    xdbg("SCRIPT: failed to execute lua function!\n");
+}
+...
+lua_run("lua_funcx", "1", "2");
+...
+
+5. Example of usage
+
+   Create your Lua script and stored on file system, say:
+   '/usr/local/etc/kamailio/lua/myscript.lua'.
+...
+function sr_append_fu_to_reply()
+        sr.hdr.append_to_reply("P-From: " .. sr.pv.get("$fu") .. "\r\n");
+end
+...
+
+   Load the script via parameter 'load' and execute function via
+   lua_run(...).
+...
+modparam("app_lua", "load", "/usr/local/etc/kamailio/lua/myscript.lua")
+...
+route {
+    ...
+    if(!lua_run("sr_append_fu_to_reply"))
+    {
+        xdbg("SCRIPT: failed to execute lua function!\n");
+    }
+    ...
+}
+...

+ 151 - 0
modules/app_lua/app_lua_mod.c

@@ -0,0 +1,151 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../mod_fix.h"
+
+#include "app_lua_sr.h"
+
+MODULE_VERSION
+
+/** parameters */
+
+/* List of allowed chars for a prefix*/
+static int  mod_init(void);
+static void mod_destroy(void);
+static int  child_init(int rank);
+
+static int w_app_lua_dostring(struct sip_msg *msg, char *script, char *extra);
+static int w_app_lua_dofile(struct sip_msg *msg, char *script, char *extra);
+static int w_app_lua_run(struct sip_msg *msg, char *func, char *p1, char *p2,
+		char *p3);
+
+int app_lua_load_param(modparam_t type, void *val);
+
+static param_export_t params[]={
+	{"load",          STR_PARAM|USE_FUNC_PARAM, (void*)app_lua_load_param},
+	{0, 0, 0}
+};
+
+static cmd_export_t cmds[]={
+	{"lua_dostring", (cmd_function)w_app_lua_dostring, 1, 0,
+		0, ANY_ROUTE},
+	{"lua_dofile", (cmd_function)w_app_lua_dofile, 1, 0,
+		0, ANY_ROUTE},
+	{"lua_run", (cmd_function)w_app_lua_run, 1, 0,
+		0, ANY_ROUTE},
+	{"lua_run", (cmd_function)w_app_lua_run, 2, 0,
+		0, ANY_ROUTE},
+	{"lua_run", (cmd_function)w_app_lua_run, 3, 0,
+		0, ANY_ROUTE},
+	{"lua_run", (cmd_function)w_app_lua_run, 4, 0,
+		0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+struct module_exports exports = {
+	"app_lua",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	0,              /* exported MI functions */
+	0,              /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+
+
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	lua_sr_init_mod();
+	return 0;
+}
+
+
+/* each child get a new connection to the database */
+static int child_init(int rank)
+{
+	if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
+		return 0; /* do nothing for the main process */
+
+	return lua_sr_init_child();
+}
+
+
+static void mod_destroy(void)
+{
+	lua_sr_destroy();
+}
+
+static int w_app_lua_dostring(struct sip_msg *msg, char *script, char *extra)
+{
+	if(!lua_sr_initialized())
+	{
+		LM_ERR("Lua env not intitialized");
+		return -1;
+	}
+	return app_lua_dostring(msg, script);
+}
+
+static int w_app_lua_dofile(struct sip_msg *msg, char *script, char *extra)
+{
+	if(!lua_sr_initialized())
+	{
+		LM_ERR("Lua env not intitialized");
+		return -1;
+	}
+	return app_lua_dofile(msg, script);
+}
+
+static int w_app_lua_run(struct sip_msg *msg, char *func, char *p1, char *p2,
+		char *p3)
+{
+	if(!lua_sr_initialized())
+	{
+		LM_ERR("Lua env not intitialized");
+		return -1;
+	}
+	return app_lua_run(msg, func, p1, p2, p3);
+}
+
+int app_lua_load_param(modparam_t type, void *val)
+{
+	if(val==NULL)
+		return -1;
+	return sr_lua_load_script((char*)val);
+}
+

+ 657 - 0
modules/app_lua/app_lua_sr.c

@@ -0,0 +1,657 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../mem/mem.h"
+#include "../../data_lump.h"
+#include "../../data_lump_rpl.h"
+#include "../../lib/kcore/cmpapi.h"
+
+#include "app_lua_sr.h"
+
+#define SRVERSION "1.0"
+
+typedef struct _sr_lua_env
+{
+	lua_State *L;
+	lua_State *LL;
+	struct sip_msg *msg;
+	unsigned int flags;
+} sr_lua_env_t;
+
+typedef struct _sr_lua_load
+{
+	char *script;
+	struct _sr_lua_load *next;
+} sr_lua_load_t;
+
+static sr_lua_env_t _sr_L_env;
+
+static sr_lua_load_t *_sr_lua_load_list = NULL;
+
+static int lua_sr_dbg (lua_State *L)
+{
+	char *txt;
+	txt = (char*)lua_tostring(L, -1);
+	if(txt!=NULL)
+		LM_DBG("%s", txt);
+	return 0;
+}
+
+static int lua_sr_err (lua_State *L)
+{
+	char *txt;
+	txt = (char*)lua_tostring(L, -1);
+	if(txt!=NULL)
+		LM_ERR("%s", txt);
+	return 0;
+}
+
+static int lua_sr_log (lua_State *L)
+{
+	char *txt;
+	char *level;
+	level = (char*)lua_tostring(L, -2);
+	txt = (char*)lua_tostring(L, -1);
+	if(txt!=NULL)
+	{
+		if(level==NULL)
+		{
+			LM_ERR("%s", txt);
+		} else {
+			if(strcasecmp(level, "warn")==0) {
+				LM_WARN("%s", txt);
+			} else if(strcasecmp(level, "crit")==0) {
+				LM_CRIT("%s", txt);
+			} else {
+				LM_ERR("%s", txt);
+			}
+		}
+	}
+	return 0;
+}
+
+static const luaL_reg _sr_core_Map [] = {
+	{"dbg", lua_sr_dbg},
+	{"err", lua_sr_err},
+	{"log", lua_sr_log},
+	{NULL, NULL}
+};
+
+static int lua_sr_hdr_append (lua_State *L)
+{
+	struct lump* anchor;
+	struct hdr_field *hf;
+	char *txt;
+	int len;
+	char *hdr;
+
+	txt = (char*)lua_tostring(L, -1);
+	if(txt==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	LM_DBG("append hf: %s\n", txt);
+	if (parse_headers(_sr_L_env.msg, HDR_EOH_F, 0) == -1)
+	{
+		LM_ERR("error while parsing message\n");
+		return 0;
+	}
+
+	hf = _sr_L_env.msg->last_header;
+	len = strlen(txt);
+	hdr = (char*)pkg_malloc(len);
+	if(hdr==NULL)
+	{
+		LM_ERR("no pkg memory left\n");
+		return 0;
+	}
+	memcpy(hdr, txt, len);
+	anchor = anchor_lump(_sr_L_env.msg,
+				hf->name.s + hf->len - _sr_L_env.msg->buf, 0, 0);
+	if(insert_new_lump_before(anchor, hdr, len, 0) == 0)
+	{
+		LM_ERR("can't insert lump\n");
+		pkg_free(hdr);
+		return 0;
+	}
+	return 0;
+}
+
+static int lua_sr_hdr_remove (lua_State *L)
+{
+	struct lump* anchor;
+	struct hdr_field *hf;
+	char *txt;
+	str hname;
+
+	txt = (char*)lua_tostring(L, -1);
+	if(txt==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	LM_DBG("remove hf: %s\n", txt);
+	if (parse_headers(_sr_L_env.msg, HDR_EOH_F, 0) == -1) {
+		LM_ERR("error while parsing message\n");
+		return 0;
+	}
+
+	hname.s = txt;
+	hname.len = strlen(txt);
+	for (hf=_sr_L_env.msg->headers; hf; hf=hf->next)
+	{
+		if (cmp_hdrname_str(&hf->name, &hname)==0)
+		{
+			anchor=del_lump(_sr_L_env.msg,
+					hf->name.s - _sr_L_env.msg->buf, hf->len, 0);
+			if (anchor==0)
+			{
+				LM_ERR("cannot remove hdr %s\n", txt);
+				return 0;
+			}
+		}
+	}
+	return 0;
+}
+
+static int lua_sr_hdr_insert (lua_State *L)
+{
+	struct lump* anchor;
+	struct hdr_field *hf;
+	char *txt;
+	int len;
+	char *hdr;
+
+	txt = (char*)lua_tostring(L, -1);
+	if(txt==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	LM_DBG("insert hf: %s\n", txt);
+	hf = _sr_L_env.msg->headers;
+	len = strlen(txt);
+	hdr = (char*)pkg_malloc(len);
+	if(hdr==NULL)
+	{
+		LM_ERR("no pkg memory left\n");
+		return 0;
+	}
+	memcpy(hdr, txt, len);
+	anchor = anchor_lump(_sr_L_env.msg,
+				hf->name.s + hf->len - _sr_L_env.msg->buf, 0, 0);
+	if(insert_new_lump_before(anchor, hdr, len, 0) == 0)
+	{
+		LM_ERR("can't insert lump\n");
+		pkg_free(hdr);
+		return 0;
+	}
+	return 0;
+}
+
+static int lua_sr_hdr_append_to_reply (lua_State *L)
+{
+	char *txt;
+	int len;
+
+	txt = (char*)lua_tostring(L, -1);
+	if(txt==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	LM_DBG("append to reply: %s\n", txt);
+	len = strlen(txt);
+
+	if(add_lump_rpl(_sr_L_env.msg, txt, len, LUMP_RPL_HDR)==0)
+	{
+		LM_ERR("unable to add reply lump\n");
+		return 0;
+	}
+
+	return 0;
+}
+
+
+static const luaL_reg _sr_hdr_Map [] = {
+	{"append", lua_sr_hdr_append},
+	{"remove", lua_sr_hdr_remove},
+	{"insert", lua_sr_hdr_insert},
+	{"append_to_reply", lua_sr_hdr_append_to_reply},
+	{NULL, NULL}
+};
+
+
+static int lua_sr_pv_get (lua_State *L)
+{
+	str pvn;
+	pv_spec_t pvs;
+    pv_value_t val;
+
+	pvn.s = (char*)lua_tostring(L, -1);
+	if(pvn.s==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv get: %s\n", pvn.s);
+	if(pv_parse_spec(&pvn, &pvs)<0)
+	{
+		LM_ERR("unable to parse pv [%s]\n", pvn.s);
+		return 0;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	if(pv_get_spec_value(_sr_L_env.msg, &pvs, &val) != 0)
+	{
+		LM_ERR("unable to get pv value for [%s]\n", pvn.s);
+		return 0;
+	}
+	if(val.flags&PV_VAL_NULL)
+	{
+		return 0;
+	}
+	if(val.flags&PV_TYPE_INT)
+	{
+		lua_pushinteger(L, val.ri);
+		return 1;
+	}
+	lua_pushlstring(L, val.rs.s, val.rs.len);
+	return 1;
+}
+
+static int lua_sr_pv_seti (lua_State *L)
+{
+	str pvn;
+	pv_spec_t pvs;
+    pv_value_t val;
+
+	if(lua_gettop(L)<2)
+	{
+		LM_ERR("to few parameters [%d]\n",lua_gettop(L));
+		return 0;
+	}
+	if(!lua_isnumber(L, -1))
+	{
+		LM_ERR("invalid int parameter\n");
+		return 0;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	val.ri = lua_tointeger(L, -1);
+	val.flags |= PV_TYPE_INT|PV_VAL_INT;
+	
+	pvn.s = (char*)lua_tostring(L, -2);
+	if(pvn.s==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv set: %s\n", pvn.s);
+	if(pv_parse_spec(&pvn, &pvs)<0)
+	{
+		LM_ERR("unable to parse pv [%s]\n", pvn.s);
+		return 0;
+	}
+	if(pv_set_spec_value(_sr_L_env.msg, &pvs, 0, &val)<0)
+	{
+		LM_ERR("unable to set pv [%s]\n", pvn.s);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int lua_sr_pv_sets (lua_State *L)
+{
+	str pvn;
+	pv_spec_t pvs;
+    pv_value_t val;
+
+	if(lua_gettop(L)<2)
+	{
+		LM_ERR("to few parameters [%d]\n",lua_gettop(L));
+		return 0;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	val.rs.s = (char*)lua_tostring(L, -1);
+	val.rs.len = strlen(val.rs.s);
+	val.flags |= PV_VAL_STR;
+	
+	pvn.s = (char*)lua_tostring(L, -2);
+	if(pvn.s==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv set: %s\n", pvn.s);
+	if(pv_parse_spec(&pvn, &pvs)<0)
+	{
+		LM_ERR("unable to parse pv [%s]\n", pvn.s);
+		return 0;
+	}
+	if(pv_set_spec_value(_sr_L_env.msg, &pvs, 0, &val)<0)
+	{
+		LM_ERR("unable to set pv [%s]\n", pvn.s);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int lua_sr_pv_unset (lua_State *L)
+{
+	str pvn;
+	pv_spec_t pvs;
+    pv_value_t val;
+
+	pvn.s = (char*)lua_tostring(L, -1);
+	if(pvn.s==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv unset: %s\n", pvn.s);
+	if(pv_parse_spec(&pvn, &pvs)<0)
+	{
+		LM_ERR("unable to parse pv [%s]\n", pvn.s);
+		return 0;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	val.flags |= PV_VAL_NULL;
+	if(pv_set_spec_value(_sr_L_env.msg, &pvs, 0, &val)<0)
+	{
+		LM_ERR("unable to unset pv [%s]\n", pvn.s);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int lua_sr_pv_is_null (lua_State *L)
+{
+	str pvn;
+	pv_spec_t pvs;
+    pv_value_t val;
+
+	pvn.s = (char*)lua_tostring(L, -1);
+	if(pvn.s==NULL || _sr_L_env.msg==NULL)
+		return 0;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv is null: %s\n", pvn.s);
+	if(pv_parse_spec(&pvn, &pvs)<0)
+	{
+		LM_ERR("unable to parse pv [%s]\n", pvn.s);
+		return 0;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	if(pv_get_spec_value(_sr_L_env.msg, &pvs, &val) != 0)
+	{
+		LM_ERR("unable to get pv value for [%s]\n", pvn.s);
+		return 0;
+	}
+	if(val.flags&PV_VAL_NULL)
+	{
+		lua_pushboolean(L, 1);
+	} else {
+		lua_pushboolean(L, 0);
+	}
+	return 1;
+}
+
+static const luaL_reg _sr_pv_Map [] = {
+	{"get",      lua_sr_pv_get},
+	{"seti",     lua_sr_pv_seti},
+	{"sets",     lua_sr_pv_sets},
+	{"unset",    lua_sr_pv_unset},
+	{"is_null",  lua_sr_pv_is_null},
+	{NULL, NULL}
+};
+
+void lua_sr_openlibs(lua_State *L)
+{
+	luaL_openlib(L, "sr",      _sr_core_Map, 0);
+	luaL_openlib(L, "sr.hdr",  _sr_hdr_Map,  0);
+	luaL_openlib(L, "sr.pv",   _sr_pv_Map,   0);
+}
+
+int sr_lua_load_script(char *script)
+{
+	sr_lua_load_t *li;
+
+	li = (sr_lua_load_t*)pkg_malloc(sizeof(sr_lua_load_t));
+	if(li==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		return -1;
+	}
+	memset(li, 0, sizeof(sr_lua_load_t));
+	li->script = script;
+	li->next = _sr_lua_load_list;
+	_sr_lua_load_list = li;
+	return 0;
+}
+
+
+int lua_sr_init_mod(void)
+{
+	memset(&_sr_L_env, 0, sizeof(sr_lua_env_t));
+	return 0;
+}
+
+int lua_sr_init_child(void)
+{
+	sr_lua_load_t *li;
+	int ret;
+	char *txt;
+	struct stat sbuf;
+
+	memset(&_sr_L_env, 0, sizeof(sr_lua_env_t));
+	_sr_L_env.L = lua_open();
+	if(_sr_L_env.L==NULL)
+	{
+		LM_ERR("cannot open lua\n");
+		return -1;
+	}
+	luaL_openlibs(_sr_L_env.L);
+	lua_sr_openlibs(_sr_L_env.L);
+
+	/* set SR lib version */
+	lua_pushstring(_sr_L_env.L, "SRVERSION");
+	lua_pushstring(_sr_L_env.L, SRVERSION);
+	lua_settable(_sr_L_env.L, LUA_GLOBALSINDEX);
+
+	if(_sr_lua_load_list != NULL)
+	{
+		_sr_L_env.LL = lua_open();
+		if(_sr_L_env.LL==NULL)
+		{
+			LM_ERR("cannot open lua loading state\n");
+			return -1;
+		}
+		luaL_openlibs(_sr_L_env.LL);
+		lua_sr_openlibs(_sr_L_env.LL);
+
+		/* set SR lib version */
+		lua_pushstring(_sr_L_env.LL, "SRVERSION");
+		lua_pushstring(_sr_L_env.LL, SRVERSION);
+		lua_settable(_sr_L_env.LL, LUA_GLOBALSINDEX);
+
+		li = _sr_lua_load_list;
+		while(li)
+		{
+			if(stat(li->script, &sbuf)!=0)
+			{
+				/* file does not exist */
+				LM_ERR("cannot find script: %s (wrong path?)\n",
+						li->script);
+				lua_sr_destroy();
+				return -1;
+			}
+ 			ret = luaL_loadfile(_sr_L_env.LL, (const char*)li->script);
+			if(ret!=0)
+			{
+				LM_ERR("failed to load Lua script: %s (err: %d)\n",
+						li->script, ret);
+				txt = (char*)lua_tostring(_sr_L_env.LL, -1);
+				LM_ERR("error from Lua: %s\n", (txt)?txt:"unknown");
+				lua_pop(_sr_L_env.LL, 1);
+				lua_sr_destroy();
+				return -1;
+			}
+			ret = lua_pcall(_sr_L_env.LL, 0, 0, 0);
+			if(ret!=0)
+			{
+				LM_ERR("failed to init Lua script: %s (err: %d)\n",
+						li->script, ret);
+				txt = (char*)lua_tostring(_sr_L_env.LL, -1);
+				LM_ERR("error from Lua: %s\n", (txt)?txt:"unknown");
+				lua_pop(_sr_L_env.LL, 1);
+				lua_sr_destroy();
+				return -1;
+			}
+			li = li->next;
+		}
+	}
+	LM_DBG("Lua initialized!\n");
+	return 0;
+}
+
+void lua_sr_destroy(void)
+{
+	if(_sr_L_env.L!=NULL)
+	{
+		lua_close(_sr_L_env.L);
+		_sr_L_env.L = NULL;
+	}
+	if(_sr_L_env.LL!=NULL)
+	{
+		lua_close(_sr_L_env.LL);
+		_sr_L_env.LL = NULL;
+	}
+	memset(&_sr_L_env, 0, sizeof(sr_lua_env_t));
+}
+
+int lua_sr_initialized(void)
+{
+	if(_sr_L_env.L==NULL)
+		return 0;
+
+	return 1;
+}
+
+int app_lua_dostring(struct sip_msg *msg, char *script)
+{
+	int ret;
+	char *txt;
+
+	LM_DBG("executing Lua string: [[%s]]\n", script);
+	LM_DBG("lua top index is: %d\n", lua_gettop(_sr_L_env.L));
+	_sr_L_env.msg = msg;
+	ret = luaL_dostring(_sr_L_env.L, script);
+	if(ret!=0)
+	{
+		txt = (char*)lua_tostring(_sr_L_env.L, -1);
+		LM_ERR("error from Lua: %s\n", (txt)?txt:"unknown");
+		lua_pop (_sr_L_env.L, 1);
+	}
+	_sr_L_env.msg = 0;
+	return (ret==0)?1:-1;
+}
+
+int app_lua_dofile(struct sip_msg *msg, char *script)
+{
+	int ret;
+	char *txt;
+
+	LM_DBG("executing Lua file: [[%s]]\n", script);
+	LM_DBG("lua top index is: %d\n", lua_gettop(_sr_L_env.L));
+	_sr_L_env.msg = msg;
+	ret = luaL_dofile(_sr_L_env.L, script);
+	if(ret!=0)
+	{
+		txt = (char*)lua_tostring(_sr_L_env.L, -1);
+		LM_ERR("error from Lua: %s\n", (txt)?txt:"unknown");
+		lua_pop(_sr_L_env.L, 1);
+	}
+	_sr_L_env.msg = 0;
+	return (ret==0)?1:-1;
+}
+
+int app_lua_run(struct sip_msg *msg, char *func, char *p1, char *p2,
+		char *p3)
+{
+	int n;
+	int ret;
+	char *txt;
+
+	if(_sr_L_env.LL==NULL)
+	{
+		LM_ERR("lua loading state not initialized (call: %s)\n", func);
+		return -1;
+	}
+
+	LM_DBG("executing Lua function: [[%s]]\n", func);
+	LM_DBG("lua top index is: %d\n", lua_gettop(_sr_L_env.LL));
+	lua_getglobal(_sr_L_env.LL, func);
+	if(!lua_isfunction(_sr_L_env.LL, -1))
+	{
+		LM_ERR("no such function [%s] in lua scripts\n", func);
+		LM_ERR("top stack type [%d - %s]\n",
+				lua_type(_sr_L_env.LL, -1),
+				lua_typename(_sr_L_env.LL,lua_type(_sr_L_env.LL, -1)));
+		txt = (char*)lua_tostring(_sr_L_env.LL, -1);
+		LM_ERR("error from Lua: %s\n", (txt)?txt:"unknown");
+		return -1;
+	}
+	n = 0;
+	if(p1!=NULL)
+	{
+		lua_pushstring(_sr_L_env.LL, p1);
+		n++;
+		if(p2!=NULL)
+		{
+			lua_pushstring(_sr_L_env.LL, p2);
+			n++;
+			if(p3!=NULL)
+			{
+				lua_pushstring(_sr_L_env.LL, p3);
+				n++;
+			}
+		}
+	}
+	_sr_L_env.msg = msg;
+	ret = lua_pcall(_sr_L_env.LL, n, 0, 0);
+	_sr_L_env.msg = 0;
+	if(ret!=0)
+	{
+		LM_ERR("error executing: %s (err: %d)\n", func, ret);
+		txt = (char*)lua_tostring(_sr_L_env.LL, -1);
+		LM_ERR("error from Lua: %s\n", (txt)?txt:"unknown");
+		lua_pop(_sr_L_env.LL, 1);
+		return -1;
+	}
+
+	return 1;
+}
+

+ 42 - 0
modules/app_lua/app_lua_sr.h

@@ -0,0 +1,42 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _APP_LUA_SR_H_
+#define _APP_LUA_SR_H_
+
+#include "../../parser/msg_parser.h"
+
+int lua_sr_initialized(void);
+int lua_sr_init_mod(void);
+int lua_sr_init_child(void);
+void lua_sr_destroy(void);
+
+int sr_lua_load_script(char *script);
+
+int app_lua_dostring(struct sip_msg *msg, char *script);
+int app_lua_dofile(struct sip_msg *msg, char *script);
+int app_lua_run(struct sip_msg *msg, char *func, char *p1, char *p2,
+		char *p3);
+
+#endif
+

+ 4 - 0
modules/app_lua/doc/Makefile

@@ -0,0 +1,4 @@
+docs = app_lua.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 36 - 0
modules/app_lua/doc/app_lua.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>app_lua Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<affiliation><orgname>asipto.com</orgname></affiliation>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		    <email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2010</year>
+	    <holder>Daniel-Constantin Mierla (asipto.com)</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+	<xi:include href="app_lua_admin.xml"/>
+    
+</book>

+ 206 - 0
modules/app_lua/doc/app_lua_admin.xml

@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<!-- Module User's Guide -->
+
+<chapter>
+    
+    <title>&adminguide;</title>
+    
+    <section>
+	<title>Overview</title>
+	<para>
+		This module allows executing Lua scripts from config file. It exports
+		a set of functions to Lua in order to access the current processed
+		SIP message. These functions are within Lua module 'sr'.
+	</para>
+	<para>
+		Lua (http://www.lua.org) is a fast and easy to embed scripting
+		language. Exported API from SIP router to Lua is documented in the
+		dokuwiki.
+	</para>
+	<para>
+		The module has two Lua contexts:
+    	<itemizedlist>
+	    <listitem>
+		<para>
+			<emphasis>first</emphasis> is used for functions lua_dofile()
+			and lua_dostring().
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    <emphasis>second</emphasis> is used for function lua_run()
+			and parameter 'load'. Therefore lua_run() cannot execute functions
+			from scripts loaded via lua_dofile() in config. This is kind of
+			caching mode, avoiding reading file every time, but you must be sure
+			you do not have someting that is executed by default and requires
+			access to SIP message.
+		</para>
+	    </listitem>
+    	</itemizedlist>
+	</para>
+    </section>
+    <section>
+	<title>Dependencies</title>
+	<section>
+	    <title>&kamailio; Modules</title>
+	    <para>
+		The following modules must be loaded before this module:
+	    	<itemizedlist>
+		    <listitem>
+			<para>
+			    <emphasis>none</emphasis>.
+			</para>
+		    </listitem>
+	    	</itemizedlist>
+	    </para>
+	</section>
+	<section>
+	    <title>External Libraries or Applications</title>
+	    <para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+	    	<itemizedlist>
+		    <listitem>
+			<para>
+			    <emphasis>liblua5.1-dev</emphasis> - Lua devel library.
+			</para>
+		    </listitem>
+	    	</itemizedlist>
+	    </para>
+	</section>
+    </section>
+    <section>
+	<title>Exported Parameters</title>
+	<section>
+	    <title><varname>load</varname> (string)</title>
+	    <para>
+			Set the path to the Lua script to be loaded at startup. Then you
+			can use lua_run(function, params) to execute a function from the
+			script at runtime.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>null</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>load</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("lua", "lua", "/usr/local/etc/kamailio/lua/myscript.lua")
+...
+</programlisting>
+	    </example>
+	</section>
+
+	</section>
+	
+    <section>
+	<title>Exported Functions</title>
+ 	<section>
+	    <title>
+		<function moreinfo="none">lua_dofile(path)</function>
+	    </title>
+	    <para>
+		Execute the Lua script stored in 'path'.
+	    </para>
+		<example>
+		<title><function>lua_dofile</function> usage</title>
+		<programlisting format="linespecific">
+...
+lua_dofile("/usr/local/etc/kamailio/lua/myscript.lua");
+...
+</programlisting>
+	    </example>
+	</section>
+	
+ 	<section>
+	    <title>
+		<function moreinfo="none">lua_dostring(script)</function>
+	    </title>
+	    <para>
+		Execute the Lua script stored in parameter.
+	    </para>
+		<example>
+		<title><function>lua_doscript</function> usage</title>
+		<programlisting format="linespecific">
+...
+if(!lua_dostring("sr.log([[err]], [[----------- Hello World\n]])"))
+{
+    xdbg("SCRIPT: failed to execute lua script!\n");
+}
+...
+</programlisting>
+	    </example>
+	</section>
+	
+	<section>
+	    <title>
+		<function moreinfo="none">lua_run(function, params)</function>
+	    </title>
+	    <para>
+		Execute the Lua function 'func' giving params as parameters. There
+		can be up to 3 string parameters. The function must exist in the
+		script loaded at startup via parameter 'load'. So far the parameters
+		are considered static strings (allowing pv is in the roadmap).
+	    </para>
+		<example>
+		<title><function>lua_doscript</function> usage</title>
+		<programlisting format="linespecific">
+...
+if(!lua_run("sr_append_fu_to_reply"))
+{
+    xdbg("SCRIPT: failed to execute lua function!\n");
+}
+...
+lua_run("lua_funcx", "1", "2");
+...
+</programlisting>
+	    </example>
+	</section>
+	
+    </section>
+	
+    <section>
+	<title>Example of usage</title>
+    <para>
+		Create your Lua script and stored on file system,
+		say: '/usr/local/etc/kamailio/lua/myscript.lua'.
+    </para>
+<programlisting format="linespecific">
+...
+function sr_append_fu_to_reply()
+	sr.hdr.append_to_reply("P-From: " .. sr.pv.get("$fu") .. "\r\n");
+end
+...
+</programlisting>
+    <para>
+		Load the script via parameter 'load' and execute function
+		via lua_run(...).
+    </para>
+<programlisting format="linespecific">
+...
+modparam("app_lua", "load", "/usr/local/etc/kamailio/lua/myscript.lua")
+...
+route {
+    ...
+    if(!lua_run("sr_append_fu_to_reply"))
+    {
+        xdbg("SCRIPT: failed to execute lua function!\n");
+    }
+    ...
+}
+...
+</programlisting>
+    </section>
+</chapter>
+