85c12662190df0093fc16e47f472108795aaea03
[project/ubus.git] / lua / ubus.c
1 /*
2  * Copyright (C) 2012 Jo-Philipp Wich <jow@openwrt.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License version 2.1
6  * as published by the Free Software Foundation
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #include <unistd.h>
15 #include <libubus.h>
16 #include <libubox/blobmsg.h>
17 #include <lauxlib.h>
18
19
20 #define MODNAME        "ubus"
21 #define METANAME       MODNAME ".meta"
22
23
24 struct ubus_lua_connection {
25         int timeout;
26         struct blob_buf buf;
27         struct ubus_context *ctx;
28 };
29
30
31 static int
32 ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table);
33
34 static int
35 ubus_lua_parse_blob_array(lua_State *L, struct blob_attr *attr, int len, bool table)
36 {
37         int rv;
38         int idx = 1;
39         int rem = len;
40         struct blob_attr *pos;
41
42         lua_newtable(L);
43
44         __blob_for_each_attr(pos, attr, rem)
45         {
46                 rv = ubus_lua_parse_blob(L, pos, table);
47
48                 if (rv > 1)
49                         lua_rawset(L, -3);
50                 else if (rv > 0)
51                         lua_rawseti(L, -2, idx++);
52         }
53
54         return 1;
55 }
56
57 static int
58 ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table)
59 {
60         int len;
61         int off = 0;
62         void *data;
63         char buf[32];
64
65         if (!blobmsg_check_attr(attr, false))
66                 return 0;
67
68         if (table && blobmsg_name(attr)[0])
69         {
70                 lua_pushstring(L, blobmsg_name(attr));
71                 off++;
72         }
73
74         data = blobmsg_data(attr);
75         len = blobmsg_data_len(attr);
76
77         switch (blob_id(attr))
78         {
79         case BLOBMSG_TYPE_BOOL:
80                 lua_pushboolean(L, *(uint8_t *)data);
81                 break;
82
83         case BLOBMSG_TYPE_INT16:
84                 lua_pushinteger(L, be16_to_cpu(*(uint16_t *)data));
85                 break;
86
87         case BLOBMSG_TYPE_INT32:
88                 lua_pushinteger(L, be32_to_cpu(*(uint32_t *)data));
89                 break;
90
91         case BLOBMSG_TYPE_INT64:
92                 /* NB: Lua cannot handle 64bit, format value as string and push that */
93                 sprintf(buf, "%lld", (long long int) be64_to_cpu(*(uint64_t *)data));
94                 lua_pushstring(L, buf);
95                 break;
96
97         case BLOBMSG_TYPE_STRING:
98                 lua_pushstring(L, data);
99                 break;
100
101         case BLOBMSG_TYPE_ARRAY:
102                 ubus_lua_parse_blob_array(L, data, len, false);
103                 break;
104
105         case BLOBMSG_TYPE_TABLE:
106                 ubus_lua_parse_blob_array(L, data, len, true);
107                 break;
108
109         default:
110                 lua_pushnil(L);
111                 break;
112         }
113
114         return off + 1;
115 }
116
117
118 static bool
119 ubus_lua_format_blob_is_array(lua_State *L)
120 {
121         lua_Integer prv = 0;
122         lua_Integer cur = 0;
123
124         /* Find out whether table is array-like */
125         for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
126         {
127                 if (lua_type(L, -2) != LUA_TNUMBER && lua_type(L, -2) != LUA_TINT)
128                 {
129                         lua_pop(L, 1);
130                         return false;
131                 }
132
133                 cur = lua_tointeger(L, -2);
134
135                 if ((cur - 1) != prv)
136                 {
137                         lua_pop(L, 1);
138                         return false;
139                 }
140
141                 prv = cur;
142         }
143
144         return true;
145 }
146
147 static int
148 ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table);
149
150 static int
151 ubus_lua_format_blob(lua_State *L, struct blob_buf *b, bool table)
152 {
153         void *c;
154         bool rv = true;
155         const char *key = table ? lua_tostring(L, -2) : NULL;
156
157         switch (lua_type(L, -1))
158         {
159         case LUA_TBOOLEAN:
160                 blobmsg_add_u8(b, key, (uint8_t)lua_toboolean(L, -1));
161                 break;
162
163         case LUA_TINT:
164         case LUA_TNUMBER:
165                 blobmsg_add_u32(b, key, (uint32_t)lua_tointeger(L, -1));
166                 break;
167
168         case LUA_TSTRING:
169         case LUA_TUSERDATA:
170         case LUA_TLIGHTUSERDATA:
171                 blobmsg_add_string(b, key, lua_tostring(L, -1));
172                 break;
173
174         case LUA_TTABLE:
175                 if (ubus_lua_format_blob_is_array(L))
176                 {
177                         c = blobmsg_open_array(b, key);
178                         rv = ubus_lua_format_blob_array(L, b, false);
179                         blobmsg_close_array(b, c);
180                 }
181                 else
182                 {
183                         c = blobmsg_open_table(b, key);
184                         rv = ubus_lua_format_blob_array(L, b, true);
185                         blobmsg_close_table(b, c);
186                 }
187                 break;
188
189         default:
190                 rv = false;
191                 break;
192         }
193
194         return rv;
195 }
196
197 static int
198 ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table)
199 {
200         for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
201         {
202                 if (!ubus_lua_format_blob(L, b, table))
203                 {
204                         lua_pop(L, 1);
205                         return false;
206                 }
207         }
208
209         return true;
210 }
211
212
213 static int
214 ubus_lua_connect(lua_State *L)
215 {
216         struct ubus_lua_connection *c;
217         const char *sockpath = luaL_optstring(L, 1, NULL);
218         int timeout = luaL_optint(L, 2, 30);
219
220         if ((c = lua_newuserdata(L, sizeof(*c))) != NULL &&
221                 (c->ctx = ubus_connect(sockpath)) != NULL)
222         {
223                 c->timeout = timeout;
224                 memset(&c->buf, 0, sizeof(c->buf));
225                 luaL_getmetatable(L, METANAME);
226                 lua_setmetatable(L, -2);
227                 return 1;
228         }
229
230         /* NB: no errors from ubus_connect() yet */
231         lua_pushnil(L);
232         lua_pushinteger(L, UBUS_STATUS_UNKNOWN_ERROR);
233         return 2;
234 }
235
236
237 static void
238 ubus_lua_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p)
239 {
240         lua_State *L = (lua_State *)p;
241
242         lua_pushstring(L, o->path);
243         lua_rawseti(L, -2, lua_objlen(L, -2) + 1);
244 }
245
246 static int
247 ubus_lua_objects(lua_State *L)
248 {
249         int rv;
250         struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME);
251
252         lua_newtable(L);
253         rv = ubus_lookup(c->ctx, NULL, ubus_lua_objects_cb, L);
254
255         if (rv != UBUS_STATUS_OK)
256         {
257                 lua_pop(L, 1);
258                 lua_pushnil(L);
259                 lua_pushinteger(L, rv);
260                 return 2;
261         }
262
263         return 1;
264 }
265
266
267 static void
268 ubus_lua_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p)
269 {
270         lua_State *L = (lua_State *)p;
271
272         if (!o->signature)
273                 return;
274
275         ubus_lua_parse_blob_array(L, blob_data(o->signature), blob_len(o->signature), true);
276 }
277
278 static int
279 ubus_lua_signatures(lua_State *L)
280 {
281         int rv;
282         struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME);
283         const char *path = luaL_checkstring(L, 2);
284
285         rv = ubus_lookup(c->ctx, path, ubus_lua_signatures_cb, L);
286
287         if (rv != UBUS_STATUS_OK)
288         {
289                 lua_pop(L, 1);
290                 lua_pushnil(L);
291                 lua_pushinteger(L, rv);
292                 return 2;
293         }
294
295         return 1;
296 }
297
298
299 static void
300 ubus_lua_call_cb(struct ubus_request *req, int type, struct blob_attr *msg)
301 {
302         lua_State *L = (lua_State *)req->priv;
303
304         if (!msg)
305                 lua_pushnil(L);
306
307         ubus_lua_parse_blob_array(L, blob_data(msg), blob_len(msg), true);
308 }
309
310 static int
311 ubus_lua_call(lua_State *L)
312 {
313         int rv;
314         uint32_t id;
315         struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME);
316         const char *path = luaL_checkstring(L, 2);
317         const char *func = luaL_checkstring(L, 3);
318
319         luaL_checktype(L, 4, LUA_TTABLE);
320         blob_buf_init(&c->buf, 0);
321
322         if (!ubus_lua_format_blob_array(L, &c->buf, true))
323         {
324                 lua_pushnil(L);
325                 lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT);
326                 return 2;
327         }
328
329         rv = ubus_lookup_id(c->ctx, path, &id);
330
331         if (rv)
332         {
333                 lua_pushnil(L);
334                 lua_pushinteger(L, rv);
335                 return 2;
336         }
337
338         rv = ubus_invoke(c->ctx, id, func, c->buf.head, ubus_lua_call_cb, L, c->timeout * 1000);
339
340         if (rv != UBUS_STATUS_OK)
341         {
342                 lua_pop(L, 1);
343                 lua_pushnil(L);
344                 lua_pushinteger(L, rv);
345                 return 2;
346         }
347
348         return 1;
349 }
350
351
352 static int
353 ubus_lua__gc(lua_State *L)
354 {
355         struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME);
356
357         if (c->ctx != NULL)
358         {
359                 ubus_free(c->ctx);
360                 memset(c, 0, sizeof(*c));
361         }
362
363         return 0;
364 }
365
366 static const luaL_Reg ubus[] = {
367         { "connect", ubus_lua_connect },
368         { "objects", ubus_lua_objects },
369         { "signatures", ubus_lua_signatures },
370         { "call", ubus_lua_call },
371         { "close", ubus_lua__gc },
372         { "__gc", ubus_lua__gc },
373         { NULL, NULL },
374 };
375
376
377 int
378 luaopen_ubus(lua_State *L)
379 {
380         /* create metatable */
381         luaL_newmetatable(L, METANAME);
382
383         /* metatable.__index = metatable */
384         lua_pushvalue(L, -1);
385         lua_setfield(L, -2, "__index");
386
387         /* fill metatable */
388         luaL_register(L, NULL, ubus);
389         lua_pop(L, 1);
390
391         /* create module */
392         luaL_register(L, MODNAME, ubus);
393
394         return 0;
395 }