1 /*
2  * (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *          Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 
11 #include <l4/sys/err.h>
12 #include <l4/re/util/br_manager>
13 #include <l4/re/util/object_registry>
14 #include <l4/re/error_helper>
15 #include <l4/ned/cmd_control>
16 
17 #include <lua.h>
18 #include <lauxlib.h>
19 #include <lualib.h>
20 
21 #include <cstdio>
22 #include <cstring>
23 #include <unistd.h>
24 #include <getopt.h>
25 
26 #include "lua_cap.h"
27 #include "lua.h"
28 
29 static Lua::Lib *_lua_init;
30 
31 extern char const _binary_ned_lua_start[];
32 extern char const _binary_ned_lua_end[];
33 
_create_table(lua_State * l)34 static int _create_table(lua_State *l)
35 { lua_newtable(l); return 1; }
36 
lua_require_module(lua_State * l,char const * name)37 void Lua::lua_require_module(lua_State *l, char const *name)
38 {
39   luaL_requiref(l, name, _create_table, 0);
40 }
41 
Lib(Prio prio)42 Lua::Lib::Lib(Prio prio) : _prio(prio), _next(0)
43 {
44   Lib **f = &_lua_init;
45   while (*f && (*f)->prio() < prio)
46     f = &(*f)->_next;
47 
48   _next = *f;
49   *f = this;
50 }
51 
52 void
run_init(lua_State * l)53 Lua::Lib::run_init(lua_State *l)
54 {
55   for (Lib *c = _lua_init; c; c = c->_next)
56     c->init(l);
57 }
58 
59 
60 static const luaL_Reg libs[] =
61 {
62   { "_G", luaopen_base },
63   {LUA_LOADLIBNAME, luaopen_package},
64   {LUA_TABLIBNAME, luaopen_table},
65 // { LUA_IOLIBNAME, luaopen_io },
66   { LUA_STRLIBNAME, luaopen_string },
67   {LUA_LOADLIBNAME, luaopen_package},
68   {LUA_DBLIBNAME, luaopen_debug},
69   { NULL, NULL }
70 };
71 
72 static char const *const options = "+e:c:";
73 static struct option const loptions[] =
74   {
75     { "execute", 1, NULL, 'e' },
76     { "cmdcap", 1, NULL, 'c' },
77     { 0, 0, 0, 0 }
78   };
79 
80 static int
execute_lua_buf(lua_State * l,char const * buf,size_t sz,char const * name)81 execute_lua_buf(lua_State *l, char const *buf, size_t sz, char const *name)
82 {
83   if (luaL_loadbuffer(l, buf, sz, name))
84     {
85       fprintf(stderr, "lua error: %s.\n", lua_tostring(l, -1));
86       lua_pop(l, lua_gettop(l));
87       return 1;
88     }
89 
90   if (lua_pcall(l, 0, 1, 0))
91     {
92       fprintf(stderr, "lua error: %s.\n", lua_tostring(l, -1));
93       lua_pop(l, lua_gettop(l));
94       return 1;
95     }
96 
97   lua_pop(l, lua_gettop(l));
98 
99   return 0;
100 }
101 
102 class Command_dispatcher
103 : public L4::Epiface_t<Command_dispatcher, L4Re::Ned::Cmd_control>
104 {
105 public:
Command_dispatcher(lua_State * l)106   Command_dispatcher(lua_State *l) : _lua(l) {}
107 
op_execute(L4Re::Ned::Cmd_control::Rights,L4::Ipc::String_in_buf<> cmd,L4::Ipc::Array_ref<char> & res)108   long op_execute(L4Re::Ned::Cmd_control::Rights,
109                   L4::Ipc::String_in_buf<> cmd,
110                   L4::Ipc::Array_ref<char> &res)
111   {
112     if (luaL_loadbuffer(_lua, cmd.data, cmd.length - 1, "argument"))
113       {
114         fprintf(stderr, "lua couldn't parse '%.*s': %s.\n", (int)cmd.length - 1,
115                 cmd.data, lua_tostring(_lua, -1));
116         lua_pop(_lua, 1);
117 
118         return -L4_EINVAL;
119       }
120 
121     if (lua_pcall(_lua, 0, 1, 0))
122       {
123         // errors during Factory::create() are returned as a table with
124         // { "msg" = message, "code" = return value }
125         // extract error message from table and push it on the stack
126         if (lua_istable(_lua, -1))
127           lua_getfield(_lua, -1, "msg");
128 
129         fprintf(stderr, "lua couldn't execute '%.*s': %s.\n", (int)cmd.length - 1,
130                 cmd.data, lua_tostring(_lua, -1));
131         lua_pop(_lua, 1);
132 
133         return -L4_EIO;
134       }
135 
136     size_t len;
137     char const *res_txt = luaL_tolstring(_lua, -1, &len);
138     if (len > res.length)
139       len = res.length;
140 
141     memcpy(res.data, res_txt,  len);
142     res.length = len;
143 
144     // drop the return value
145     lua_pop(_lua, lua_gettop(_lua));
146 
147     return L4_EOK;
148   }
149 
150 private:
151   lua_State *_lua;
152 };
153 
154 namespace Lua { namespace {
155 
__server_loop(lua_State * l)156 static int __server_loop(lua_State *l)
157 {
158   static bool once;
159 
160   if (once)
161     return 0;
162   once = true;
163 
164   Lua::Cap *n = check_cap(l, 1);
165 
166   L4Re::Util::Registry_server<L4Re::Util::Br_manager_timeout_hooks> server;
167   Command_dispatcher cmd_dispatch(l);
168 
169   L4Re::chkcap(server.registry()->register_obj(&cmd_dispatch,
170                                                n->cap<L4::Ipc_gate>().get()),
171                "Register command dispatcher endpoint.");
172 
173   server.loop();
174 
175   return 0;
176 }
177 
178 class Lib_server : public Lib
179 {
180 public:
Lib_server()181   Lib_server() : Lib(P_env) {}
182 
init(lua_State * l)183   void init(lua_State *l)
184   {
185     static const luaL_Reg _ops[] =
186     {
187       { "server_loop", __server_loop },
188       { NULL, NULL }
189     };
190     Lua::lua_require_module(l, "L4");
191     luaL_setfuncs(l, _ops, 0);
192   }
193 };
194 static Lib_server __libserver;
195 
196 }}
197 
lua(int argc,char const * const * argv)198 int lua(int argc, char const *const *argv)
199 {
200   printf("Ned says: Hi World!\n");
201 
202   enum Mode { None, Cmd_channel };
203   Mode mode = None;
204 
205   lua_State *L;
206   L = luaL_newstate();
207 
208   if (!L)
209     return 1;
210 
211   for (int i = 0; libs[i].func; ++i)
212     {
213       luaL_requiref(L, libs[i].name, libs[i].func, 1);
214       lua_pop(L, 1);
215     }
216 
217   Lua::init_lua_cap(L);
218   Lua::Lib::run_init(L);
219 
220   if (execute_lua_buf(L, _binary_ned_lua_start,
221                       _binary_ned_lua_end - _binary_ned_lua_start,
222                       "@ned.lua"))
223     return 0;
224 
225   int opt;
226   char const *cmd_client = 0;
227   while ((opt = getopt_long(argc, const_cast<char *const*>(argv),
228                             options, loptions, NULL)) != -1)
229     {
230       switch (opt)
231         {
232         case 'e':
233           {
234             int err = execute_lua_buf(L, optarg, strlen(optarg), optarg);
235             if (err)
236               fprintf(stderr, "Error executing cmdline statement\n");
237             break;
238           }
239         case 'c':
240           cmd_client = optarg;
241           mode = Cmd_channel;
242           break;
243         default: break;
244         }
245     }
246 
247   // everything following the first non-option argument is considered an
248   // argument for the Lua script and added to the 'arg' table
249   // The script name itself is passed as arg[0].
250   if (argc > optind)
251     {
252       if (!_create_table(L))
253         {
254           fprintf(stderr, "lua error: %s.\n", lua_tostring(L, -1));
255           lua_pop(L, lua_gettop(L));
256           return 0;
257         }
258 
259       unsigned arg_idx = 0;
260       for (int c = optind; c < argc; ++c, ++arg_idx)
261         {
262           lua_pushinteger(L, arg_idx);
263           lua_pushstring(L, argv[c]);
264           lua_settable(L, -3);
265         }
266       lua_setglobal(L, "arg");
267     }
268 
269   printf("Ned: loading file: '%s'\n", argv[optind]);
270   int e = luaL_dofile(L, argv[optind]);
271   if (e)
272     {
273       // errors during Factory::create() are returned as a table with
274       // { "msg" = message, "code" = return value }
275       // extract error message from table and push it on the stack
276       if (lua_istable(L, -1))
277           lua_getfield(L, -1, "msg");
278 
279       fprintf(stderr, "lua error: %s.\n", lua_tostring(L, -1));
280     }
281 
282   lua_gc(L, LUA_GCCOLLECT, 0);
283 
284   if (mode == Cmd_channel)
285     {
286       L4Re::Util::Registry_server<L4Re::Util::Br_manager_timeout_hooks> server;
287       Command_dispatcher cmd_dispatch(L);
288 
289       server.registry()->register_obj(&cmd_dispatch, cmd_client);
290 
291       server.loop();
292     }
293 
294   return 0;
295 }
296