/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see .
*/
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "grub_lib.h"
#include
#include
#include
#include
#include
#include
#ifdef ENABLE_LUA_PCI
#include
#endif
/* Updates the globals grub_errno and grub_msg, leaving their values on the
top of the stack, and clears grub_errno. When grub_errno is zero, grub_msg
is not left on the stack. The value returned is the number of values left on
the stack. */
static int
push_result (lua_State *state)
{
int saved_errno;
int num_results;
saved_errno = grub_errno;
grub_errno = 0;
/* Push once for setfield, and again to leave on the stack */
lua_pushinteger (state, saved_errno);
lua_pushinteger (state, saved_errno);
lua_setfield (state, LUA_GLOBALSINDEX, "grub_errno");
if (saved_errno)
{
/* Push once for setfield, and again to leave on the stack */
lua_pushstring (state, grub_errmsg);
lua_pushstring (state, grub_errmsg);
num_results = 2;
}
else
{
lua_pushnil (state);
num_results = 1;
}
lua_setfield (state, LUA_GLOBALSINDEX, "grub_errmsg");
return num_results;
}
/* Updates the globals grub_errno and grub_msg ( without leaving them on the
stack ), clears grub_errno, and returns the value of grub_errno before it
was cleared. */
static int
save_errno (lua_State *state)
{
int saved_errno;
saved_errno = grub_errno;
lua_pop(state, push_result(state));
return saved_errno;
}
static int
grub_lua_run (lua_State *state)
{
int n;
char **args;
const char *s;
s = luaL_checkstring (state, 1);
if ((! grub_parser_split_cmdline (s, 0, 0, &n, &args))
&& (n >= 0))
{
grub_command_t cmd;
cmd = grub_command_find (args[0]);
if (cmd)
(cmd->func) (cmd, n-1, &args[1]);
else
grub_error (GRUB_ERR_FILE_NOT_FOUND, "command not found");
grub_free (args[0]);
grub_free (args);
}
return push_result (state);
}
static int
grub_lua_getenv (lua_State *state)
{
int n, i;
n = lua_gettop (state);
for (i = 1; i <= n; i++)
{
const char *name, *value;
name = luaL_checkstring (state, i);
value = grub_env_get (name);
if (value)
lua_pushstring (state, value);
else
lua_pushnil (state);
}
return n;
}
static int
grub_lua_setenv (lua_State *state)
{
const char *name, *value;
name = luaL_checkstring (state, 1);
value = luaL_checkstring (state, 2);
if (name[0])
grub_env_set (name, value);
return 0;
}
/* Helper for grub_lua_enum_device. */
static int
grub_lua_enum_device_iter (const char *name, void *data)
{
lua_State *state = data;
int result;
grub_device_t dev;
result = 0;
dev = grub_device_open (name);
if (dev)
{
grub_fs_t fs;
fs = grub_fs_probe (dev);
if (fs)
{
lua_pushvalue (state, 1);
lua_pushstring (state, name);
lua_pushstring (state, fs->name);
if (! fs->uuid)
lua_pushnil (state);
else
{
int err;
char *uuid;
err = fs->uuid (dev, &uuid);
if (err)
{
grub_errno = 0;
lua_pushnil (state);
}
else
{
lua_pushstring (state, uuid);
grub_free (uuid);
}
}
if (! fs->label)
lua_pushnil (state);
else
{
int err;
char *label = NULL;
err = fs->label (dev, &label);
if (err)
{
grub_errno = 0;
lua_pushnil (state);
}
else
{
if (label == NULL)
{
lua_pushnil (state);
}
else
{
lua_pushstring (state, label);
}
grub_free (label);
}
}
lua_call (state, 4, 1);
result = lua_tointeger (state, -1);
lua_pop (state, 1);
}
else
grub_errno = 0;
grub_device_close (dev);
}
else
grub_errno = 0;
return result;
}
static int
grub_lua_enum_device (lua_State *state)
{
luaL_checktype (state, 1, LUA_TFUNCTION);
grub_device_iterate (grub_lua_enum_device_iter, state);
return push_result (state);
}
static int
enum_file (const char *name, const struct grub_dirhook_info *info,
void *data)
{
int result;
lua_State *state = data;
lua_pushvalue (state, 1);
lua_pushstring (state, name);
lua_pushinteger (state, info->dir != 0);
lua_call (state, 2, 1);
result = lua_tointeger (state, -1);
lua_pop (state, 1);
return result;
}
static int
grub_lua_enum_file (lua_State *state)
{
char *device_name;
const char *arg;
grub_device_t dev;
luaL_checktype (state, 1, LUA_TFUNCTION);
arg = luaL_checkstring (state, 2);
device_name = grub_file_get_device_name (arg);
dev = grub_device_open (device_name);
if (dev)
{
grub_fs_t fs;
const char *path;
fs = grub_fs_probe (dev);
path = grub_strchr (arg, ')');
if (! path)
path = arg;
else
path++;
if (fs)
{
(fs->dir) (dev, path, enum_file, state);
}
grub_device_close (dev);
}
grub_free (device_name);
return push_result (state);
}
#ifdef ENABLE_LUA_PCI
/* Helper for grub_lua_enum_pci. */
static int
grub_lua_enum_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, void *data)
{
lua_State *state = data;
int result;
grub_pci_address_t addr;
grub_uint32_t class;
lua_pushvalue (state, 1);
lua_pushinteger (state, grub_pci_get_bus (dev));
lua_pushinteger (state, grub_pci_get_device (dev));
lua_pushinteger (state, grub_pci_get_function (dev));
lua_pushinteger (state, pciid);
addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
class = grub_pci_read (addr);
lua_pushinteger (state, class);
lua_call (state, 5, 1);
result = lua_tointeger (state, -1);
lua_pop (state, 1);
return result;
}
static int
grub_lua_enum_pci (lua_State *state)
{
luaL_checktype (state, 1, LUA_TFUNCTION);
grub_pci_iterate (grub_lua_enum_pci_iter, state);
return push_result (state);
}
#endif
static int
grub_lua_file_open (lua_State *state)
{
grub_file_t file;
const char *name;
name = luaL_checkstring (state, 1);
file = grub_file_open (name);
save_errno (state);
if (! file)
return 0;
lua_pushlightuserdata (state, file);
return 1;
}
static int
grub_lua_file_close (lua_State *state)
{
grub_file_t file;
luaL_checktype (state, 1, LUA_TLIGHTUSERDATA);
file = lua_touserdata (state, 1);
grub_file_close (file);
return push_result (state);
}
static int
grub_lua_file_seek (lua_State *state)
{
grub_file_t file;
grub_off_t offset;
luaL_checktype (state, 1, LUA_TLIGHTUSERDATA);
file = lua_touserdata (state, 1);
offset = luaL_checkinteger (state, 2);
offset = grub_file_seek (file, offset);
save_errno (state);
lua_pushinteger (state, offset);
return 1;
}
static int
grub_lua_file_read (lua_State *state)
{
grub_file_t file;
luaL_Buffer b;
int n;
luaL_checktype (state, 1, LUA_TLIGHTUSERDATA);
file = lua_touserdata (state, 1);
n = luaL_checkinteger (state, 2);
luaL_buffinit (state, &b);
while (n)
{
char *p;
int nr;
nr = (n > LUAL_BUFFERSIZE) ? LUAL_BUFFERSIZE : n;
p = luaL_prepbuffer (&b);
nr = grub_file_read (file, p, nr);
if (nr <= 0)
break;
luaL_addsize (&b, nr);
n -= nr;
}
save_errno (state);
luaL_pushresult (&b);
return 1;
}
static int
grub_lua_file_getline (lua_State *state)
{
grub_file_t file;
char *line;
luaL_checktype (state, 1, LUA_TLIGHTUSERDATA);
file = lua_touserdata (state, 1);
line = grub_file_getline (file);
save_errno (state);
if (! line)
return 0;
lua_pushstring (state, line);
grub_free (line);
return 1;
}
static int
grub_lua_file_getsize (lua_State *state)
{
grub_file_t file;
luaL_checktype (state, 1, LUA_TLIGHTUSERDATA);
file = lua_touserdata (state, 1);
lua_pushinteger (state, file->size);
return 1;
}
static int
grub_lua_file_getpos (lua_State *state)
{
grub_file_t file;
luaL_checktype (state, 1, LUA_TLIGHTUSERDATA);
file = lua_touserdata (state, 1);
lua_pushinteger (state, file->offset);
return 1;
}
static int
grub_lua_file_eof (lua_State *state)
{
grub_file_t file;
luaL_checktype (state, 1, LUA_TLIGHTUSERDATA);
file = lua_touserdata (state, 1);
lua_pushboolean (state, file->offset >= file->size);
return 1;
}
static int
grub_lua_file_exist (lua_State *state)
{
grub_file_t file;
const char *name;
int result;
result = 0;
name = luaL_checkstring (state, 1);
file = grub_file_open (name);
if (file)
{
result++;
grub_file_close (file);
}
else
grub_errno = 0;
lua_pushboolean (state, result);
return 1;
}
static int
grub_lua_add_menu (lua_State *state)
{
int n;
const char *source;
source = luaL_checklstring (state, 1, 0);
n = lua_gettop (state) - 1;
if (n > 0)
{
const char **args;
char *p;
int i;
args = grub_malloc (n * sizeof (args[0]));
if (!args)
return push_result (state);
for (i = 0; i < n; i++)
args[i] = luaL_checkstring (state, 2 + i);
p = grub_strdup (source);
if (! p)
return push_result (state);
grub_normal_add_menu_entry (n, args, NULL, NULL, NULL, NULL, NULL, p, 0);
}
else
{
lua_pushstring (state, "not enough parameter");
lua_error (state);
}
return push_result (state);
}
luaL_Reg grub_lua_lib[] =
{
{"run", grub_lua_run},
{"getenv", grub_lua_getenv},
{"setenv", grub_lua_setenv},
{"enum_device", grub_lua_enum_device},
{"enum_file", grub_lua_enum_file},
#ifdef ENABLE_LUA_PCI
{"enum_pci", grub_lua_enum_pci},
#endif
{"file_open", grub_lua_file_open},
{"file_close", grub_lua_file_close},
{"file_seek", grub_lua_file_seek},
{"file_read", grub_lua_file_read},
{"file_getline", grub_lua_file_getline},
{"file_getsize", grub_lua_file_getsize},
{"file_getpos", grub_lua_file_getpos},
{"file_eof", grub_lua_file_eof},
{"file_exist", grub_lua_file_exist},
{"add_menu", grub_lua_add_menu},
{0, 0}
};