885 lines
20 KiB
C
885 lines
20 KiB
C
/* $NetBSD: lua.c,v 1.16 2015/02/07 04:09:13 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2014 by Lourival Vieira Neto <lneto@NetBSD.org>.
|
|
* Copyright (c) 2011 - 2014 by Marc Balmer <mbalmer@NetBSD.org>.
|
|
* All rights reserved.
|
|
*
|
|
* 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.
|
|
* 3. The name of the Author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE REGENTS 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.
|
|
*/
|
|
|
|
/* Lua device driver */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/condvar.h>
|
|
#include <sys/device.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/kmem.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/lua.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/namei.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/cpu.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
#include "luavar.h"
|
|
|
|
struct lua_softc {
|
|
device_t sc_dev;
|
|
|
|
kmutex_t sc_lock;
|
|
kcondvar_t sc_inuse_cv;
|
|
bool sc_inuse;
|
|
|
|
/* Locking access to state queues */
|
|
kmutex_t sc_state_lock;
|
|
kcondvar_t sc_state_cv;
|
|
bool sc_state;
|
|
|
|
struct sysctllog *sc_log;
|
|
};
|
|
|
|
static device_t sc_self;
|
|
static bool lua_autoload_on = true;
|
|
static bool lua_require_on = true;
|
|
static bool lua_bytecode_on = false;
|
|
static int lua_verbose;
|
|
static int lua_max_instr;
|
|
|
|
static LIST_HEAD(, lua_state) lua_states;
|
|
static LIST_HEAD(, lua_module) lua_modules;
|
|
|
|
static int lua_match(device_t, cfdata_t, void *);
|
|
static void lua_attach(device_t, device_t, void *);
|
|
static int lua_detach(device_t, int);
|
|
static klua_State *klua_find(const char *);
|
|
static const char *lua_reader(lua_State *, void *, size_t *);
|
|
static void lua_maxcount(lua_State *, lua_Debug *);
|
|
|
|
static int lua_require(lua_State *);
|
|
|
|
CFATTACH_DECL_NEW(lua, sizeof(struct lua_softc),
|
|
lua_match, lua_attach, lua_detach, NULL);
|
|
|
|
dev_type_open(luaopen);
|
|
dev_type_close(luaclose);
|
|
dev_type_ioctl(luaioctl);
|
|
|
|
const struct cdevsw lua_cdevsw = {
|
|
.d_open = luaopen,
|
|
.d_close = luaclose,
|
|
.d_read = noread,
|
|
.d_write = nowrite,
|
|
.d_ioctl = luaioctl,
|
|
.d_stop = nostop,
|
|
.d_tty = notty,
|
|
.d_poll = nopoll,
|
|
.d_mmap = nommap,
|
|
.d_kqfilter = nokqfilter,
|
|
.d_discard = nodiscard,
|
|
.d_flag = D_OTHER | D_MPSAFE
|
|
};
|
|
|
|
struct lua_loadstate {
|
|
struct vnode *vp;
|
|
size_t size;
|
|
off_t off;
|
|
};
|
|
|
|
extern struct cfdriver lua_cd;
|
|
|
|
static int
|
|
lua_match(device_t parent, cfdata_t match, void *aux)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
lua_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct lua_softc *sc;
|
|
const struct sysctlnode *node;
|
|
|
|
if (sc_self)
|
|
return;
|
|
|
|
sc = device_private(self);
|
|
sc->sc_dev = self;
|
|
sc_self = self;
|
|
|
|
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
|
|
cv_init(&sc->sc_inuse_cv, "luactl");
|
|
|
|
mutex_init(&sc->sc_state_lock, MUTEX_DEFAULT, IPL_VM);
|
|
cv_init(&sc->sc_state_cv, "luastate");
|
|
|
|
pmf_device_register(self, NULL, NULL);
|
|
|
|
/* Sysctl to provide some control over behaviour */
|
|
sysctl_createv(&sc->sc_log, 0, NULL, &node,
|
|
CTLFLAG_OWNDESC,
|
|
CTLTYPE_NODE, "lua",
|
|
SYSCTL_DESCR("Lua options"),
|
|
NULL, 0, NULL, 0,
|
|
CTL_KERN, CTL_CREATE, CTL_EOL);
|
|
|
|
if (node == NULL) {
|
|
printf(": can't create sysctl node\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* XXX Some of the sysctl values must not be changed after the
|
|
* securelevel has been raised.
|
|
*/
|
|
sysctl_createv(&sc->sc_log, 0, &node, NULL,
|
|
CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
|
|
CTLTYPE_BOOL, "require",
|
|
SYSCTL_DESCR("Enable the require command"),
|
|
NULL, 0, &lua_require_on, 0,
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
sysctl_createv(&sc->sc_log, 0, &node, NULL,
|
|
CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
|
|
CTLTYPE_BOOL, "autoload",
|
|
SYSCTL_DESCR("Enable automatic load of modules"),
|
|
NULL, 0, &lua_autoload_on, 0,
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
sysctl_createv(&sc->sc_log, 0, &node, NULL,
|
|
CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
|
|
CTLTYPE_BOOL, "bytecode",
|
|
SYSCTL_DESCR("Enable loading of bytecode"),
|
|
NULL, 0, &lua_bytecode_on, 0,
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
sysctl_createv(&sc->sc_log, 0, &node, NULL,
|
|
CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
|
|
CTLTYPE_INT, "verbose",
|
|
SYSCTL_DESCR("Enable verbose output"),
|
|
NULL, 0, &lua_verbose, 0,
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
sysctl_createv(&sc->sc_log, 0, &node, NULL,
|
|
CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
|
|
CTLTYPE_INT, "maxcount",
|
|
SYSCTL_DESCR("Limit maximum instruction count"),
|
|
NULL, 0, &lua_max_instr, 0,
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
aprint_normal_dev(self, "%s\n", LUA_COPYRIGHT);
|
|
}
|
|
|
|
static int
|
|
lua_detach(device_t self, int flags)
|
|
{
|
|
struct lua_softc *sc;
|
|
struct lua_state *s;
|
|
|
|
sc = device_private(self);
|
|
pmf_device_deregister(self);
|
|
|
|
if (sc->sc_log != NULL) {
|
|
sysctl_teardown(&sc->sc_log);
|
|
sc->sc_log = NULL;
|
|
}
|
|
|
|
/* Traverse the list of states and close them */
|
|
while ((s = LIST_FIRST(&lua_states)) != NULL) {
|
|
LIST_REMOVE(s, lua_next);
|
|
klua_close(s->K);
|
|
if (lua_verbose)
|
|
device_printf(self, "state %s destroyed\n",
|
|
s->lua_name);
|
|
kmem_free(s, sizeof(struct lua_state));
|
|
}
|
|
mutex_destroy(&sc->sc_lock);
|
|
cv_destroy(&sc->sc_inuse_cv);
|
|
mutex_destroy(&sc->sc_state_lock);
|
|
cv_destroy(&sc->sc_state_cv);
|
|
sc_self = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
luaopen(dev_t dev, int flag, int mode, struct lwp *l)
|
|
{
|
|
struct lua_softc *sc;
|
|
int error = 0;
|
|
|
|
if (minor(dev) > 0)
|
|
return ENXIO;
|
|
|
|
sc = device_lookup_private(&lua_cd, minor(dev));
|
|
if (sc == NULL)
|
|
return ENXIO;
|
|
|
|
mutex_enter(&sc->sc_lock);
|
|
while (sc->sc_inuse == true) {
|
|
error = cv_wait_sig(&sc->sc_inuse_cv, &sc->sc_lock);
|
|
if (error)
|
|
break;
|
|
}
|
|
if (!error)
|
|
sc->sc_inuse = true;
|
|
mutex_exit(&sc->sc_lock);
|
|
|
|
if (error)
|
|
return error;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
luaclose(dev_t dev, int flag, int mode, struct lwp *l)
|
|
{
|
|
struct lua_softc *sc;
|
|
|
|
if (minor(dev) > 0)
|
|
return ENXIO;
|
|
sc = device_lookup_private(&lua_cd, minor(dev));
|
|
mutex_enter(&sc->sc_lock);
|
|
sc->sc_inuse = false;
|
|
cv_signal(&sc->sc_inuse_cv);
|
|
mutex_exit(&sc->sc_lock);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
luaioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
|
|
{
|
|
struct lua_softc *sc;
|
|
struct lua_info *info;
|
|
struct lua_create *create;
|
|
struct lua_require *require;
|
|
struct lua_load *load;
|
|
struct lua_state *s;
|
|
struct lua_module *m;
|
|
kauth_cred_t cred;
|
|
struct nameidata nd;
|
|
struct pathbuf *pb;
|
|
struct vattr va;
|
|
struct lua_loadstate ls;
|
|
int error, n;
|
|
klua_State *K;
|
|
|
|
sc = device_lookup_private(&lua_cd, minor(dev));
|
|
if (!device_is_active(sc->sc_dev))
|
|
return EBUSY;
|
|
|
|
switch (cmd) {
|
|
case LUAINFO:
|
|
info = data;
|
|
if (info->states == NULL) {
|
|
info->num_states = 0;
|
|
LIST_FOREACH(s, &lua_states, lua_next)
|
|
info->num_states++;
|
|
} else {
|
|
n = 0;
|
|
LIST_FOREACH(s, &lua_states, lua_next) {
|
|
if (n > info->num_states)
|
|
break;
|
|
copyoutstr(s->lua_name, info->states[n].name,
|
|
MAX_LUA_NAME, NULL);
|
|
copyoutstr(s->lua_desc, info->states[n].desc,
|
|
MAX_LUA_DESC, NULL);
|
|
info->states[n].user = s->K->ks_user;
|
|
n++;
|
|
}
|
|
info->num_states = n;
|
|
}
|
|
break;
|
|
case LUACREATE:
|
|
create = data;
|
|
|
|
if (*create->name == '_') {
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev, "names of user "
|
|
"created states must not begin with '_'");
|
|
return ENXIO;
|
|
}
|
|
LIST_FOREACH(s, &lua_states, lua_next)
|
|
if (!strcmp(s->lua_name, create->name)) {
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev,
|
|
"state %s exists\n", create->name);
|
|
return EBUSY;
|
|
}
|
|
|
|
K = kluaL_newstate(create->name, create->desc, IPL_NONE);
|
|
K->ks_user = true;
|
|
|
|
if (K == NULL)
|
|
return ENOMEM;
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev, "state %s created\n",
|
|
create->name);
|
|
break;
|
|
case LUADESTROY:
|
|
create = data;
|
|
|
|
K = klua_find(create->name);
|
|
|
|
if (K != NULL && (K->ks_user == true)) {
|
|
klua_close(K);
|
|
return 0;
|
|
}
|
|
return EBUSY;
|
|
case LUAREQUIRE: /* 'require' a module in a State */
|
|
require = data;
|
|
LIST_FOREACH(s, &lua_states, lua_next)
|
|
if (!strcmp(s->lua_name, require->state))
|
|
LIST_FOREACH(m, &lua_modules, mod_next)
|
|
if (!strcmp(m->mod_name,
|
|
require->module)) {
|
|
if (lua_verbose)
|
|
device_printf(
|
|
sc->sc_dev,
|
|
"requiring module "
|
|
"%s to state %s\n",
|
|
m->mod_name,
|
|
s->lua_name);
|
|
klua_lock(s->K);
|
|
luaL_requiref(
|
|
s->K->L,
|
|
m->mod_name,
|
|
m->open,
|
|
1);
|
|
klua_unlock(s->K);
|
|
m->refcount++;
|
|
LIST_INSERT_HEAD(
|
|
&s->lua_modules, m,
|
|
mod_next);
|
|
return 0;
|
|
}
|
|
return ENXIO;
|
|
case LUALOAD:
|
|
load = data;
|
|
if (strrchr(load->path, '/') == NULL)
|
|
return ENXIO;
|
|
|
|
LIST_FOREACH(s, &lua_states, lua_next)
|
|
if (!strcmp(s->lua_name, load->state)) {
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev,
|
|
"loading %s into state %s\n",
|
|
load->path, s->lua_name);
|
|
cred = kauth_cred_get();
|
|
pb = pathbuf_create(load->path);
|
|
if (pb == NULL)
|
|
return ENOMEM;
|
|
NDINIT(&nd, LOOKUP, FOLLOW | NOCHROOT, pb);
|
|
pathbuf_destroy(pb);
|
|
error = vn_open(&nd, FREAD, 0);
|
|
if (error) {
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev,
|
|
"error vn_open %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
error = VOP_GETATTR(nd.ni_vp, &va,
|
|
kauth_cred_get());
|
|
if (error) {
|
|
VOP_UNLOCK(nd.ni_vp);
|
|
vn_close(nd.ni_vp, FREAD,
|
|
kauth_cred_get());
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev,
|
|
"erro VOP_GETATTR %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
if (va.va_type != VREG) {
|
|
VOP_UNLOCK(nd.ni_vp);
|
|
vn_close(nd.ni_vp, FREAD,
|
|
kauth_cred_get());
|
|
return EINVAL;
|
|
}
|
|
ls.vp = nd.ni_vp;
|
|
ls.off = 0L;
|
|
ls.size = va.va_size;
|
|
VOP_UNLOCK(nd.ni_vp);
|
|
klua_lock(s->K);
|
|
error = lua_load(s->K->L, lua_reader, &ls,
|
|
strrchr(load->path, '/') + 1, "bt");
|
|
vn_close(nd.ni_vp, FREAD, cred);
|
|
switch (error) {
|
|
case 0: /* no error */
|
|
break;
|
|
case LUA_ERRSYNTAX:
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev,
|
|
"syntax error\n");
|
|
klua_unlock(s->K);
|
|
return EINVAL;
|
|
case LUA_ERRMEM:
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev,
|
|
"memory error\n");
|
|
klua_unlock(s->K);
|
|
return ENOMEM;
|
|
default:
|
|
if (lua_verbose)
|
|
device_printf(sc->sc_dev,
|
|
"load error %d: %s\n",
|
|
error,
|
|
lua_tostring(s->K->L, -1));
|
|
klua_unlock(s->K);
|
|
return EINVAL;
|
|
}
|
|
if (lua_max_instr > 0)
|
|
lua_sethook(s->K->L, lua_maxcount,
|
|
LUA_MASKCOUNT, lua_max_instr);
|
|
error = lua_pcall(s->K->L, 0, LUA_MULTRET, 0);
|
|
if (error) {
|
|
if (lua_verbose) {
|
|
device_printf(sc->sc_dev,
|
|
"execution error: %s\n",
|
|
lua_tostring(s->K->L, -1));
|
|
}
|
|
klua_unlock(s->K);
|
|
return EINVAL;
|
|
}
|
|
klua_unlock(s->K);
|
|
return 0;
|
|
}
|
|
return ENXIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lua_require(lua_State *L)
|
|
{
|
|
struct lua_state *s;
|
|
struct lua_module *m, *md;
|
|
const char *module;
|
|
char name[MAXPATHLEN];
|
|
|
|
module = lua_tostring(L, -1);
|
|
md = NULL;
|
|
LIST_FOREACH(m, &lua_modules, mod_next)
|
|
if (!strcmp(m->mod_name, module)) {
|
|
md = m;
|
|
break;
|
|
}
|
|
|
|
if (md == NULL && lua_autoload_on && strchr(module, '/') == NULL) {
|
|
snprintf(name, sizeof name, "lua%s", module);
|
|
if (lua_verbose)
|
|
device_printf(sc_self, "autoload %s\n", name);
|
|
module_autoload(name, MODULE_CLASS_MISC);
|
|
LIST_FOREACH(m, &lua_modules, mod_next)
|
|
if (!strcmp(m->mod_name, module)) {
|
|
md = m;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (md != NULL)
|
|
LIST_FOREACH(s, &lua_states, lua_next)
|
|
if (s->K->L == L) {
|
|
if (lua_verbose)
|
|
device_printf(sc_self,
|
|
"require module %s\n",
|
|
md->mod_name);
|
|
luaL_requiref(L, md->mod_name, md->open, 0);
|
|
|
|
md->refcount++;
|
|
LIST_INSERT_HEAD(&s->lua_modules, md, mod_next);
|
|
return 1;
|
|
}
|
|
|
|
lua_pushstring(L, "module not found");
|
|
return lua_error(L);
|
|
}
|
|
|
|
typedef struct {
|
|
size_t size;
|
|
} __packed alloc_header_t;
|
|
|
|
static void *
|
|
lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
|
|
{
|
|
void *nptr = NULL;
|
|
|
|
const size_t hdr_size = sizeof(alloc_header_t);
|
|
alloc_header_t *hdr = (alloc_header_t *) ((char *) ptr - hdr_size);
|
|
|
|
if (nsize == 0) { /* freeing */
|
|
if (ptr != NULL)
|
|
kmem_intr_free(hdr, hdr->size);
|
|
} else if (ptr != NULL && nsize <= hdr->size - hdr_size) /* shrinking */
|
|
return ptr; /* don't need to reallocate */
|
|
else { /* creating or expanding */
|
|
km_flag_t sleep = cpu_intr_p() || cpu_softintr_p() ?
|
|
KM_NOSLEEP : KM_SLEEP;
|
|
|
|
size_t alloc_size = nsize + hdr_size;
|
|
alloc_header_t *nhdr = kmem_intr_alloc(alloc_size, sleep);
|
|
if (nhdr == NULL) /* failed to allocate */
|
|
return NULL;
|
|
|
|
nhdr->size = alloc_size;
|
|
nptr = (void *) ((char *) nhdr + hdr_size);
|
|
|
|
if (ptr != NULL) { /* expanding */
|
|
memcpy(nptr, ptr, osize);
|
|
kmem_intr_free(hdr, hdr->size);
|
|
}
|
|
}
|
|
return nptr;
|
|
}
|
|
|
|
static const char *
|
|
lua_reader(lua_State *L, void *data, size_t *size)
|
|
{
|
|
struct lua_loadstate *ls;
|
|
static char buf[1024];
|
|
size_t rsiz;
|
|
|
|
ls = data;
|
|
if (ls->size < sizeof(buf))
|
|
rsiz = ls->size;
|
|
else
|
|
rsiz = sizeof(buf);
|
|
vn_rdwr(UIO_READ, ls->vp, buf, rsiz, ls->off, UIO_SYSSPACE,
|
|
0, curlwp->l_cred, NULL, curlwp);
|
|
if (ls->off == 0L && lua_bytecode_on == false && buf[0] == 0x1b) {
|
|
*size = 0L;
|
|
lua_pushstring(L, "loading of bytecode is not allowed");
|
|
lua_error(L);
|
|
return NULL;
|
|
} else {
|
|
*size = rsiz;
|
|
ls->off += *size;
|
|
ls->size -= *size;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static void
|
|
lua_maxcount(lua_State *L, lua_Debug *d)
|
|
{
|
|
lua_pushstring(L, "maximum instruction count exceeded");
|
|
lua_error(L);
|
|
}
|
|
|
|
int
|
|
klua_mod_register(const char *name, lua_CFunction open)
|
|
{
|
|
struct lua_module *m;
|
|
|
|
LIST_FOREACH(m, &lua_modules, mod_next)
|
|
if (!strcmp(m->mod_name, name))
|
|
return EBUSY;
|
|
m = kmem_zalloc(sizeof(struct lua_module), KM_SLEEP);
|
|
strlcpy(m->mod_name, name, LUA_MAX_MODNAME);
|
|
m->open = open;
|
|
m->refcount = 0;
|
|
LIST_INSERT_HEAD(&lua_modules, m, mod_next);
|
|
if (lua_verbose)
|
|
device_printf(sc_self, "registered lua module %s\n", name);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
klua_mod_unregister(const char *name)
|
|
{
|
|
struct lua_module *m;
|
|
|
|
LIST_FOREACH(m, &lua_modules, mod_next)
|
|
if (!strcmp(m->mod_name, name)) {
|
|
if (m->refcount == 0) {
|
|
LIST_REMOVE(m, mod_next);
|
|
kmem_free(m, sizeof(struct lua_module));
|
|
if (lua_verbose)
|
|
device_printf(sc_self,
|
|
"unregistered lua module %s\n",
|
|
name);
|
|
return 0;
|
|
} else
|
|
return EBUSY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
klua_State *
|
|
klua_newstate(lua_Alloc f, void *ud, const char *name, const char *desc,
|
|
int ipl)
|
|
{
|
|
klua_State *K;
|
|
struct lua_state *s;
|
|
struct lua_softc *sc;
|
|
int error = 0;
|
|
|
|
s = kmem_zalloc(sizeof(struct lua_state), KM_SLEEP);
|
|
sc = device_private(sc_self);
|
|
mutex_enter(&sc->sc_state_lock);
|
|
while (sc->sc_state == true) {
|
|
error = cv_wait_sig(&sc->sc_state_cv, &sc->sc_state_lock);
|
|
if (error)
|
|
break;
|
|
}
|
|
if (!error)
|
|
sc->sc_state = true;
|
|
mutex_exit(&sc->sc_state_lock);
|
|
|
|
if (error) {
|
|
kmem_free(s, sizeof(struct lua_state));
|
|
return NULL;
|
|
}
|
|
|
|
K = kmem_zalloc(sizeof(klua_State), KM_SLEEP);
|
|
K->L = lua_newstate(f, ud);
|
|
K->ks_user = false;
|
|
if (K->L == NULL) {
|
|
kmem_free(K, sizeof(klua_State));
|
|
K = NULL;
|
|
goto finish;
|
|
}
|
|
|
|
strlcpy(s->lua_name, name, MAX_LUA_NAME);
|
|
strlcpy(s->lua_desc, desc, MAX_LUA_DESC);
|
|
s->K = K;
|
|
|
|
if (lua_require_on || lua_autoload_on) {
|
|
lua_pushcfunction(K->L, lua_require);
|
|
lua_setglobal(K->L, "require");
|
|
}
|
|
LIST_INSERT_HEAD(&lua_states, s, lua_next);
|
|
|
|
mutex_init(&K->ks_lock, MUTEX_DEFAULT, ipl);
|
|
|
|
finish:
|
|
mutex_enter(&sc->sc_state_lock);
|
|
sc->sc_state = false;
|
|
cv_signal(&sc->sc_state_cv);
|
|
mutex_exit(&sc->sc_state_lock);
|
|
return K;
|
|
}
|
|
|
|
inline klua_State *
|
|
kluaL_newstate(const char *name, const char *desc, int ipl)
|
|
{
|
|
return klua_newstate(lua_alloc, NULL, name, desc, ipl);
|
|
}
|
|
|
|
void
|
|
klua_close(klua_State *K)
|
|
{
|
|
struct lua_state *s;
|
|
struct lua_softc *sc;
|
|
struct lua_module *m;
|
|
int error = 0;
|
|
|
|
/* XXX consider registering a handler instead of a fixed name. */
|
|
lua_getglobal(K->L, "onClose");
|
|
if (lua_isfunction(K->L, -1))
|
|
lua_pcall(K->L, -1, 0, 0);
|
|
|
|
sc = device_private(sc_self);
|
|
mutex_enter(&sc->sc_state_lock);
|
|
while (sc->sc_state == true) {
|
|
error = cv_wait_sig(&sc->sc_state_cv, &sc->sc_state_lock);
|
|
if (error)
|
|
break;
|
|
}
|
|
if (!error)
|
|
sc->sc_state = true;
|
|
mutex_exit(&sc->sc_state_lock);
|
|
|
|
if (error)
|
|
return; /* Nothing we can do... */
|
|
|
|
LIST_FOREACH(s, &lua_states, lua_next)
|
|
if (s->K == K) {
|
|
LIST_REMOVE(s, lua_next);
|
|
LIST_FOREACH(m, &s->lua_modules, mod_next)
|
|
m->refcount--;
|
|
kmem_free(s, sizeof(struct lua_state));
|
|
}
|
|
|
|
lua_close(K->L);
|
|
mutex_destroy(&K->ks_lock);
|
|
kmem_free(K, sizeof(klua_State));
|
|
|
|
mutex_enter(&sc->sc_state_lock);
|
|
sc->sc_state = false;
|
|
cv_signal(&sc->sc_state_cv);
|
|
mutex_exit(&sc->sc_state_lock);
|
|
}
|
|
|
|
static klua_State *
|
|
klua_find(const char *name)
|
|
{
|
|
struct lua_state *s;
|
|
struct lua_softc *sc;
|
|
klua_State *K;
|
|
int error = 0;
|
|
|
|
K = NULL;
|
|
sc = device_private(sc_self);
|
|
mutex_enter(&sc->sc_state_lock);
|
|
while (sc->sc_state == true) {
|
|
error = cv_wait_sig(&sc->sc_state_cv, &sc->sc_state_lock);
|
|
if (error)
|
|
break;
|
|
}
|
|
if (!error)
|
|
sc->sc_state = true;
|
|
mutex_exit(&sc->sc_state_lock);
|
|
|
|
if (error)
|
|
return NULL;
|
|
|
|
LIST_FOREACH(s, &lua_states, lua_next)
|
|
if (!strcmp(s->lua_name, name)) {
|
|
K = s->K;
|
|
break;
|
|
}
|
|
|
|
mutex_enter(&sc->sc_state_lock);
|
|
sc->sc_state = false;
|
|
cv_signal(&sc->sc_state_cv);
|
|
mutex_exit(&sc->sc_state_lock);
|
|
return K;
|
|
}
|
|
|
|
inline void
|
|
klua_lock(klua_State *K)
|
|
{
|
|
mutex_enter(&K->ks_lock);
|
|
}
|
|
|
|
inline void
|
|
klua_unlock(klua_State *K)
|
|
{
|
|
mutex_exit(&K->ks_lock);
|
|
}
|
|
|
|
MODULE(MODULE_CLASS_MISC, lua, NULL);
|
|
|
|
#ifdef _MODULE
|
|
static const struct cfiattrdata luabus_iattrdata = {
|
|
"luabus", 0, { { NULL, NULL, 0 },}
|
|
};
|
|
|
|
static const struct cfiattrdata *const lua_attrs[] = {
|
|
&luabus_iattrdata, NULL
|
|
};
|
|
|
|
CFDRIVER_DECL(lua, DV_DULL, lua_attrs);
|
|
extern struct cfattach lua_ca;
|
|
static int lualoc[] = {
|
|
-1,
|
|
-1,
|
|
-1
|
|
};
|
|
|
|
static struct cfdata lua_cfdata[] = {
|
|
{
|
|
.cf_name = "lua",
|
|
.cf_atname = "lua",
|
|
.cf_unit = 0,
|
|
.cf_fstate = FSTATE_STAR,
|
|
.cf_loc = lualoc,
|
|
.cf_flags = 0,
|
|
.cf_pspec = NULL,
|
|
},
|
|
{ NULL, NULL, 0, FSTATE_NOTFOUND, NULL, 0, NULL }
|
|
};
|
|
#endif
|
|
|
|
static int
|
|
lua_modcmd(modcmd_t cmd, void *opaque)
|
|
{
|
|
#ifdef _MODULE
|
|
devmajor_t cmajor, bmajor;
|
|
int error = 0;
|
|
|
|
cmajor = bmajor = NODEVMAJOR;
|
|
#endif
|
|
switch (cmd) {
|
|
case MODULE_CMD_INIT:
|
|
#ifdef _MODULE
|
|
error = config_cfdriver_attach(&lua_cd);
|
|
if (error)
|
|
return error;
|
|
|
|
error = config_cfattach_attach(lua_cd.cd_name,
|
|
&lua_ca);
|
|
if (error) {
|
|
config_cfdriver_detach(&lua_cd);
|
|
aprint_error("%s: unable to register cfattach\n",
|
|
lua_cd.cd_name);
|
|
return error;
|
|
}
|
|
error = config_cfdata_attach(lua_cfdata, 1);
|
|
if (error) {
|
|
config_cfattach_detach(lua_cd.cd_name,
|
|
&lua_ca);
|
|
config_cfdriver_detach(&lua_cd);
|
|
aprint_error("%s: unable to register cfdata\n",
|
|
lua_cd.cd_name);
|
|
return error;
|
|
}
|
|
error = devsw_attach(lua_cd.cd_name, NULL, &bmajor,
|
|
&lua_cdevsw, &cmajor);
|
|
if (error) {
|
|
aprint_error("%s: unable to register devsw\n",
|
|
lua_cd.cd_name);
|
|
config_cfattach_detach(lua_cd.cd_name, &lua_ca);
|
|
config_cfdriver_detach(&lua_cd);
|
|
return error;
|
|
}
|
|
config_attach_pseudo(lua_cfdata);
|
|
#endif
|
|
return 0;
|
|
case MODULE_CMD_FINI:
|
|
#ifdef _MODULE
|
|
error = config_cfdata_detach(lua_cfdata);
|
|
if (error)
|
|
return error;
|
|
|
|
config_cfattach_detach(lua_cd.cd_name, &lua_ca);
|
|
config_cfdriver_detach(&lua_cd);
|
|
devsw_detach(NULL, &lua_cdevsw);
|
|
#endif
|
|
return 0;
|
|
case MODULE_CMD_AUTOUNLOAD:
|
|
/* no auto-unload */
|
|
return EBUSY;
|
|
default:
|
|
return ENOTTY;
|
|
}
|
|
}
|