5922e0470697186a7920693a7e760a0b9cb21602
[project/libubox.git] / lua / uloop.c
1 /*
2  * Copyright (C) 2012 John Crispin <blogic@openwrt.org>
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20
21 #include <lua.h>
22 #include <lualib.h>
23 #include <lauxlib.h>
24
25 #include "../uloop.h"
26 #include "../list.h"
27
28 struct lua_uloop_timeout {
29         struct uloop_timeout t;
30         int r;
31 };
32
33 struct lua_uloop_process {
34         struct uloop_process p;
35         int r;
36 };
37
38 static lua_State *state;
39
40 static void ul_timer_cb(struct uloop_timeout *t)
41 {
42         struct lua_uloop_timeout *tout = container_of(t, struct lua_uloop_timeout, t);
43
44         lua_getglobal(state, "__uloop_cb");
45         lua_rawgeti(state, -1, tout->r);
46         lua_remove(state, -2);
47         lua_call(state, 0, 0);
48 }
49
50 static int ul_timer_set(lua_State *L)
51 {
52         struct lua_uloop_timeout *tout;
53         double set;
54
55         if (!lua_isnumber(L, -1)) {
56                 lua_pushstring(L, "invalid arg list");
57                 lua_error(L);
58
59                 return 0;
60         }
61
62         set = lua_tointeger(L, -1);
63         tout = lua_touserdata(L, 1);
64         uloop_timeout_set(&tout->t, set);
65
66         return 1;
67 }
68
69 static int ul_timer_free(lua_State *L)
70 {
71         struct lua_uloop_timeout *tout = lua_touserdata(L, 1);
72
73         uloop_timeout_cancel(&tout->t);
74         lua_getglobal(state, "__uloop_cb");
75         luaL_unref(L, -1, tout->r);
76
77         return 1;
78 }
79
80 static const luaL_Reg timer_m[] = {
81         { "set", ul_timer_set },
82         { "cancel", ul_timer_free },
83         { NULL, NULL }
84 };
85
86 static int ul_timer(lua_State *L)
87 {
88         struct lua_uloop_timeout *tout;
89         int set = 0;
90         int ref;
91
92         if (lua_isnumber(L, -1)) {
93                 set = lua_tointeger(L, -1);
94                 lua_pop(L, 1);
95         }
96
97         if (!lua_isfunction(L, -1)) {
98                 lua_pushstring(L, "invalid arg list");
99                 lua_error(L);
100
101                 return 0;
102         }
103
104         lua_getglobal(L, "__uloop_cb");
105         lua_pushvalue(L, -2);
106         ref = luaL_ref(L, -2);
107
108         tout = lua_newuserdata(L, sizeof(*tout));
109         lua_createtable(L, 0, 2);
110         lua_pushvalue(L, -1);
111         lua_setfield(L, -2, "__index");
112         lua_pushcfunction(L, ul_timer_free);
113         lua_setfield(L, -2, "__gc");
114         lua_pushvalue(L, -1);
115         lua_setmetatable(L, -3);
116         lua_pushvalue(L, -2);
117         luaI_openlib(L, NULL, timer_m, 1);
118         lua_pushvalue(L, -2);
119
120         memset(tout, 0, sizeof(*tout));
121
122         tout->r = ref;
123         tout->t.cb = ul_timer_cb;
124         if (set)
125                 uloop_timeout_set(&tout->t, set);
126
127         return 1;
128 }
129
130 static void ul_process_cb(struct uloop_process *p, int ret)
131 {
132         struct lua_uloop_process *proc = container_of(p, struct lua_uloop_process, p);
133
134         lua_getglobal(state, "__uloop_cb");
135         lua_rawgeti(state, -1, proc->r);
136         luaL_unref(state, -2, proc->r);
137         lua_remove(state, -2);
138         lua_pushinteger(state, ret >> 8);
139         lua_call(state, 1, 0);
140 }
141
142 static int ul_process(lua_State *L)
143 {
144         struct lua_uloop_process *proc;
145         pid_t pid;
146         int ref;
147
148         if (!lua_isfunction(L, -1) || !lua_istable(L, -2) ||
149                         !lua_istable(L, -3) || !lua_isstring(L, -4)) {
150                 lua_pushstring(L, "invalid arg list");
151                 lua_error(L);
152
153                 return 0;
154         }
155
156         pid = fork();
157
158         if (pid == -1) {
159                 lua_pushstring(L, "failed to fork");
160                 lua_error(L);
161
162                 return 0;
163         }
164
165         if (pid == 0) {
166                 /* child */
167                 int argn = lua_objlen(L, -3);
168                 int envn = lua_objlen(L, -2);
169                 char** argp = malloc(sizeof(char*) * (argn + 2));
170                 char** envp = malloc(sizeof(char*) * envn + 1);
171                 int i = 1;
172
173                 argp[0] = (char*) lua_tostring(L, -4);
174                 for (i = 1; i <= argn; i++) {
175                         lua_rawgeti(L, -3, i);
176                         argp[i] = (char*) lua_tostring(L, -1);
177                         lua_pop(L, 1);
178                 }
179                 argp[i] = NULL;
180
181                 for (i = 1; i <= envn; i++) {
182                         lua_rawgeti(L, -2, i);
183                         envp[i - 1] = (char*) lua_tostring(L, -1);
184                         lua_pop(L, 1);
185                 }
186                 envp[i - 1] = NULL;
187
188                 execve(*argp, argp, envp);
189                 exit(-1);
190         }
191
192         lua_getglobal(L, "__uloop_cb");
193         lua_pushvalue(L, -2);
194         ref = luaL_ref(L, -2);
195
196         proc = lua_newuserdata(L, sizeof(*proc));
197         memset(proc, 0, sizeof(*proc));
198
199         proc->r = ref;
200         proc->p.pid = pid;
201         proc->p.cb = ul_process_cb;
202         uloop_process_add(&proc->p);
203
204         return 1;
205 }
206
207 static int ul_init(lua_State *L)
208 {
209         uloop_init();
210         lua_pushboolean(L, 1);
211
212         return 1;
213 }
214
215 static int ul_run(lua_State *L)
216 {
217         uloop_run();
218         lua_pushboolean(L, 1);
219
220         return 1;
221 }
222
223 static luaL_reg uloop_func[] = {
224         {"init", ul_init},
225         {"run", ul_run},
226         {"timer", ul_timer},
227         {"process", ul_process},
228         {NULL, NULL},
229 };
230
231 /* avoid warnings about missing declarations */
232 int luaopen_uloop(lua_State *L);
233 int luaclose_uloop(lua_State *L);
234
235 int luaopen_uloop(lua_State *L)
236 {
237         state = L;
238
239         lua_createtable(L, 1, 0);
240         lua_setglobal(L, "__uloop_cb");
241
242         luaL_openlib(L, "uloop", uloop_func, 0);
243         lua_pushstring(L, "_VERSION");
244         lua_pushstring(L, "1.0");
245         lua_rawset(L, -3);
246
247         return 1;
248 }
249
250 int luaclose_uloop(lua_State *L)
251 {
252         lua_pushstring(L, "Called");
253
254         return 1;
255 }