/* $%BEGINLICENSE%$
Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved.
This program 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; version 2 of the
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
$%ENDLICENSE%$ */
#include
#include
#include
#include
#include
#include /* got g_stat() */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_LUA_H
#include
#include
#include
#endif
#include "lua-load-factory.h"
#include "lua-scope.h"
#include "chassis-stats.h"
static int proxy_lua_panic (lua_State *L);
static void *chassis_lua_alloc(void *userdata, void *ptr, size_t osize, size_t nsize);
/**
* @deprecated will be removed in 1.0
* @see lua_scope_new()
*/
lua_scope *lua_scope_init(void) {
return lua_scope_new();
}
lua_scope *lua_scope_new(void) {
lua_scope *sc;
sc = g_new0(lua_scope, 1);
#ifdef HAVE_LUA_H
sc->L = lua_newstate(chassis_lua_alloc, NULL);
luaL_openlibs(sc->L);
lua_atpanic(sc->L, proxy_lua_panic);
#endif
sc->mutex = g_mutex_new(); /*remove lock*/
return sc;
}
void lua_scope_free(lua_scope *sc) {
if (!sc) return;
#ifdef HAVE_LUA_H
/**
* enforce that the stack is clean
*
* we still have items on the stack
*/
if (lua_gettop(sc->L) != 0) {
g_critical("%s: lua-scope has %d items on the stack",
G_STRLOC,
lua_gettop(sc->L));
}
/* FIXME: we might want to cleanup the cached-scripts in the registry */
lua_close(sc->L);
#endif
g_mutex_free(sc->mutex); /*remove lock*/
g_free(sc);
}
void lua_scope_get(lua_scope *sc, const char G_GNUC_UNUSED* pos) {
/* g_warning("%s: === waiting for lua-scope", pos); */
g_mutex_lock(sc->mutex);
/* g_warning("%s: +++ got lua-scope", pos); */
#ifdef HAVE_LUA_H
sc->L_top = lua_gettop(sc->L);
#endif
return;
}
void lua_scope_release(lua_scope *sc, const char* pos) {
#ifdef HAVE_LUA_H
if (lua_gettop(sc->L) != sc->L_top) {
g_critical("%s: lua-stack out of sync: is %d, should be %d", pos, lua_gettop(sc->L), sc->L_top);
}
#endif
g_mutex_unlock(sc->mutex);
/* g_warning("%s: --- released lua scope", pos); */
return;
}
#ifdef HAVE_LUA_H
/**
* load the lua script
*
* wraps luaL_loadfile and prints warnings when needed
*
* on success we leave a function on the stack, otherwise a error-msg
*
* @see luaL_loadfile
* @returns the lua_State
*/
lua_State *lua_scope_load_script(lua_scope *sc, const gchar *name) {
lua_State *L = sc->L;
int stack_top = lua_gettop(L);
/**
* check if the script is in the cache already
*
* if it is and is fresh, duplicate it
* otherwise load it and put it in the cache
*/
#if 1
lua_getfield(L, LUA_REGISTRYINDEX, "cachedscripts"); /* sp += 1 */
if (lua_isnil(L, -1)) {
/** oops, not there yet */
lua_pop(L, 1); /* sp -= 1 */
lua_newtable(L); /* reg.cachedscripts = { } sp += 1 */
lua_setfield(L, LUA_REGISTRYINDEX, "cachedscripts"); /* sp -= 1 */
lua_getfield(L, LUA_REGISTRYINDEX, "cachedscripts"); /* sp += 1 */
}
g_assert(lua_istable(L, -1)); /** the script-cache should be on the stack now */
g_assert(lua_gettop(L) == stack_top + 1);
/**
* reg.
* cachedscripts. <- on the stack
* .
* mtime
* func
*/
lua_getfield(L, -1, name);
if (lua_istable(L, -1)) {
struct stat st;
time_t cached_mtime;
off_t cached_size;
/** the script cached, check that it is fresh */
if (0 != g_stat(name, &st)) {
gchar *errmsg;
/* stat() failed, ... not good */
lua_pop(L, 2); /* cachedscripts. + cachedscripts. */
errmsg = g_strdup_printf("%s: stat(%s) failed: %s (%d)",
G_STRLOC, name, g_strerror(errno), errno);
lua_pushstring(L, errmsg);
g_free(errmsg);
g_assert(lua_isstring(L, -1));
g_assert(lua_gettop(L) == stack_top + 1);
return L;
}
/* get the mtime from the table */
lua_getfield(L, -1, "mtime");
g_assert(lua_isnumber(L, -1));
cached_mtime = lua_tonumber(L, -1);
lua_pop(L, 1);
/* get the mtime from the table */
lua_getfield(L, -1, "size");
g_assert(lua_isnumber(L, -1));
cached_size = lua_tonumber(L, -1);
lua_pop(L, 1);
if (st.st_mtime != cached_mtime ||
st.st_size != cached_size) {
lua_pushnil(L);
lua_setfield(L, -2, "func"); /* zap the old function on the stack */
if (0 != luaL_loadfile_factory(L, name)) {
/* log a warning and leave the error-msg on the stack */
g_warning("%s: reloading '%s' failed", G_STRLOC, name);
/* cleanup a bit */
lua_remove(L, -2); /* remove the cachedscripts. */
lua_remove(L, -2); /* remove cachedscripts-table */
g_assert(lua_isstring(L, -1));
g_assert(lua_gettop(L) == stack_top + 1);
return L;
}
lua_setfield(L, -2, "func");
/* not fresh, reload */
lua_pushinteger(L, st.st_mtime);
lua_setfield(L, -2, "mtime"); /* t.mtime = ... */
lua_pushinteger(L, st.st_size);
lua_setfield(L, -2, "size"); /* t.size = ... */
}
} else if (lua_isnil(L, -1)) {
struct stat st;
lua_pop(L, 1); /* remove the nil, aka not found */
/** not known yet */
lua_newtable(L); /* t = { } */
if (0 != g_stat(name, &st)) {
gchar *errmsg;
/* stat() failed, ... not good */
errmsg = g_strdup_printf("%s: stat(%s) failed: %s (%d)",
G_STRLOC, name, g_strerror(errno), errno);
lua_pop(L, 2); /* cachedscripts. + cachedscripts. */
lua_pushstring(L, errmsg);
g_free(errmsg);
g_assert(lua_isstring(L, -1));
g_assert(lua_gettop(L) == stack_top + 1);
return L;
}
if (0 != luaL_loadfile_factory(L, name)) {
/* leave the error-msg on the stack */
/* cleanup a bit */
lua_remove(L, -2); /* remove the t = { } */
lua_remove(L, -2); /* remove cachedscripts-table */
g_assert(lua_isstring(L, -1));
g_assert(lua_gettop(L) == stack_top + 1);
return L;
}
lua_setfield(L, -2, "func");
lua_pushinteger(L, st.st_mtime);
lua_setfield(L, -2, "mtime"); /* t.mtime = ... */
lua_pushinteger(L, st.st_size);
lua_setfield(L, -2, "size"); /* t.size = ... */
lua_setfield(L, -2, name); /* reg.cachedscripts. = t */
lua_getfield(L, -1, name);
} else {
/* not good */
lua_pushstring(L, "stack is out of sync");
g_return_val_if_reached(L);
}
/* -- the cache is fresh now, get the script from it */
g_assert(lua_istable(L, -1));
lua_getfield(L, -1, "func");
g_assert(lua_isfunction(L, -1));
/* cachedscripts and are still on the stack */
#if 0
g_debug("(load) [-3] %s", lua_typename(L, lua_type(L, -3)));
g_debug("(load) [-2] %s", lua_typename(L, lua_type(L, -2)));
g_debug("(load) [-1] %s", lua_typename(L, lua_type(L, -1)));
#endif
lua_remove(L, -2); /* remove the reg.cachedscripts. */
lua_remove(L, -2); /* remove the reg.cachedscripts */
/* create a copy of the script for us:
*
* f = function ()
* return function ()
*