src/utils_lua.[ch]: Implement functions to export C types to Lua.
[collectd.git] / src / utils_lua.c
1 /**
2  * collectd - src/utils_lua.c
3  * Copyright (C) 2010       Florian Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; only version 2.1 of the License is
8  * applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Authors:
20  *   Florian Forster <octo at collectd.org>
21  **/
22
23 #include "utils_lua.h"
24 #include "common.h"
25
26 static int ltoc_values (lua_State *l, /* {{{ */
27     const data_set_t *ds,
28     value_t *ret_values)
29 {
30   size_t i;
31   int status = 0;
32
33   if (!lua_istable (l, -1))
34     return (-1);
35
36   /* Push initial key */
37   lua_pushnil (l);
38   for (i = 0; i < ((size_t) ds->ds_num); i++)
39   {
40     /* Pops old key and pushed new key and value. */
41     status = lua_next (l, -2);
42     if (status == 0) /* no more elements */
43       break;
44
45     ret_values[i] = luaC_tovalue (l, /* idx = */ -1, ds->ds[i].type);
46
47     /* Pop the value */
48     lua_pop (l, /* nelems = */ 1);
49   }
50   /* Pop the key */
51   lua_pop (l, /* nelems = */ 1);
52
53   return (status);
54 } /* }}} int ltoc_values */
55
56 static int ltoc_table_values (lua_State *l, int idx, /* {{{ */
57     const data_set_t *ds, value_list_t *vl)
58 {
59   int status;
60
61   /* We're only called from "luaC_tovaluelist", which ensures that "idx" is an
62    * absolute index (i.e. a positive number) */
63   assert (idx > 0);
64
65   lua_pushstring (l, "values");
66   lua_gettable (l, idx);
67
68   if (!lua_istable (l, -1))
69   {
70     NOTICE ("lua plugin: ltoc_table_values: The \"values\" member is not a table.");
71     lua_pop (l, /* nelem = */ 1);
72     return (-1);
73   }
74
75   vl->values_len = ds->ds_num;
76   vl->values = calloc ((size_t) vl->values_len, sizeof (*vl->values));
77   if (vl->values == NULL)
78   {
79     ERROR ("lua plugin: calloc failed.");
80     vl->values_len = 0;
81     lua_pop (l, /* nelem = */ 1);
82     return (-1);
83   }
84
85   status = ltoc_values (l, ds, vl->values);
86
87   lua_pop (l, /* nelem = */ 1);
88
89   if (status != 0)
90   {
91     vl->values_len = 0;
92     sfree (vl->values);
93   }
94
95   return (status);
96 } /* }}} int ltoc_table_values */
97
98 static int luaC_pushvalues (lua_State *l, const data_set_t *ds, const value_list_t *vl) /* {{{ */
99 {
100   int i;
101
102   assert (vl->values_len == ds->ds_num);
103
104   lua_newtable (l);
105   for (i = 0; i < vl->values_len; i++)
106   {
107     lua_pushinteger (l, (lua_Integer) i);
108     luaC_pushvalue (l, vl->values[i], ds->ds[i].type);
109     lua_settable (l, /* idx = */ -3);
110   }
111
112   return (0);
113 } /* }}} int luaC_pushvalues */
114
115 static int luaC_pushdstypes (lua_State *l, const data_set_t *ds) /* {{{ */
116 {
117   int i;
118
119   lua_newtable (l);
120   for (i = 0; i < ds->ds_num; i++)
121   {
122     lua_pushinteger (l, (lua_Integer) i);
123     lua_pushstring (l, DS_TYPE_TO_STRING (ds->ds[i].type));
124     lua_settable (l, /* idx = */ -3);
125   }
126
127   return (0);
128 } /* }}} int luaC_pushdstypes */
129
130 static int luaC_pushdsnames (lua_State *l, const data_set_t *ds) /* {{{ */
131 {
132   int i;
133
134   lua_newtable (l);
135   for (i = 0; i < ds->ds_num; i++)
136   {
137     lua_pushinteger (l, (lua_Integer) i);
138     lua_pushstring (l, ds->ds[i].name);
139     lua_settable (l, /* idx = */ -3);
140   }
141
142   return (0);
143 } /* }}} int luaC_pushdsnames */
144
145 /*
146  * Public functions
147  */
148 cdtime_t luaC_tocdtime (lua_State *l, int idx) /* {{{ */
149 {
150   double d;
151
152   if (!lua_isnumber (l, /* stack pos = */ idx))
153     return (0);
154
155   d = (double) lua_tonumber (l, idx);
156
157   return (DOUBLE_TO_CDTIME_T (d));
158 } /* }}} int ltoc_table_cdtime */
159
160 int luaC_tostringbuffer (lua_State *l, int idx, /* {{{ */
161     char *buffer, size_t buffer_size)
162 {
163   const char *str;
164
165   str = lua_tostring (l, idx);
166   if (str == NULL)
167     return (-1);
168
169   sstrncpy (buffer, str, buffer_size);
170   return (0);
171 } /* }}} int luaC_tostringbuffer */
172
173 value_t luaC_tovalue (lua_State *l, int idx, int ds_type) /* {{{ */
174 {
175   value_t v;
176
177   memset (&v, 0, sizeof (v));
178
179   if (!lua_isnumber (l, idx))
180     return (v);
181
182   if (ds_type == DS_TYPE_GAUGE)
183     v.gauge = (gauge_t) lua_tonumber (l, /* stack pos = */ -1);
184   else if (ds_type == DS_TYPE_DERIVE)
185     v.derive = (derive_t) lua_tointeger (l, /* stack pos = */ -1);
186   else if (ds_type == DS_TYPE_COUNTER)
187     v.counter = (counter_t) lua_tointeger (l, /* stack pos = */ -1);
188   else if (ds_type == DS_TYPE_ABSOLUTE)
189     v.absolute = (absolute_t) lua_tointeger (l, /* stack pos = */ -1);
190
191   return (v);
192 } /* }}} value_t luaC_tovalue */
193
194 value_list_t *luaC_tovaluelist (lua_State *l, int idx) /* {{{ */
195 {
196   const data_set_t *ds;
197   value_list_t *vl;
198   int status;
199 #if COLLECT_DEBUG
200   int stack_top_before = lua_gettop (l);
201 #endif
202
203   /* Convert relative indexes to absolute indexes, so it doesn't change when we
204    * push / pop stuff. */
205   if (idx < 1)
206     idx += lua_gettop (l) + 1;
207
208   /* Check that idx is in the valid range */
209   if ((idx < 1) || (idx > lua_gettop (l)))
210     return (NULL);
211
212   vl = malloc (sizeof (*vl));
213   if (vl == NULL)
214     return (NULL);
215   memset (vl, 0, sizeof (*vl));
216   vl->values = NULL;
217   vl->meta = NULL;
218
219   /* Push initial key */
220   lua_pushnil (l);
221   while (lua_next (l, idx) != 0)
222   {
223     const char *key = lua_tostring (l, /* stack pos = */ -2);
224
225     if (key == NULL)
226     {
227       DEBUG ("luaC_tovaluelist: Ignoring non-string key.");
228     }
229     else if (strcasecmp ("host", key) == 0)
230       luaC_tostringbuffer (l, /* idx = */ -1,
231           vl->host, sizeof (vl->host));
232     else if (strcasecmp ("plugin", key) == 0)
233       luaC_tostringbuffer (l, /* idx = */ -1,
234           vl->plugin, sizeof (vl->plugin));
235     else if (strcasecmp ("plugin_instance", key) == 0)
236       luaC_tostringbuffer (l, /* idx = */ -1,
237           vl->plugin_instance, sizeof (vl->plugin_instance));
238     else if (strcasecmp ("type", key) == 0)
239       luaC_tostringbuffer (l, /* idx = */ -1,
240           vl->type, sizeof (vl->type));
241     else if (strcasecmp ("type_instance", key) == 0)
242       luaC_tostringbuffer (l, /* idx = */ -1,
243           vl->type_instance, sizeof (vl->type_instance));
244     else if (strcasecmp ("time", key) == 0)
245       vl->time = luaC_tocdtime (l, -1);
246     else if (strcasecmp ("interval", key) == 0)
247       vl->interval = luaC_tocdtime (l, -1);
248     else if (strcasecmp ("values", key) == 0)
249     {
250       /* This key is not handled here, because we have to assure "type" is read
251        * first. */
252     }
253     else
254     {
255       DEBUG ("luaC_tovaluelist: Ignoring unknown key \"%s\".", key);
256     }
257
258     /* Pop the value */
259     lua_pop (l, 1);
260   }
261   /* Pop the key */
262   lua_pop (l, 1);
263
264   ds = plugin_get_ds (vl->type);
265   if (ds == NULL)
266   {
267     INFO ("lua plugin: Unable to lookup type \"%s\".", vl->type);
268     sfree (vl);
269     return (NULL);
270   }
271
272   status = ltoc_table_values (l, idx, ds, vl);
273   if (status != 0)
274   {
275     WARNING ("lua plugin: ltoc_table_values failed.");
276     sfree (vl);
277     return (NULL);
278   }
279
280 #if COLLECT_DEBUG
281   assert (stack_top_before == lua_gettop (l));
282 #endif
283   return (vl);
284 } /* }}} value_list_t *luaC_tovaluelist */
285
286 int luaC_pushcdtime (lua_State *l, cdtime_t t) /* {{{ */
287 {
288   double d = CDTIME_T_TO_DOUBLE (t);
289
290   lua_pushnumber (l, (lua_Number) d);
291   return (0);
292 } /* }}} int luaC_pushcdtime */
293
294 int luaC_pushvalue (lua_State *l, value_t v, int ds_type) /* {{{ */
295 {
296   if (ds_type == DS_TYPE_GAUGE)
297     lua_pushnumber (l, (lua_Number) v.gauge);
298   else if (ds_type == DS_TYPE_DERIVE)
299     lua_pushinteger (l, (lua_Integer) v.derive);
300   else if (ds_type == DS_TYPE_COUNTER)
301     lua_pushinteger (l, (lua_Integer) v.counter);
302   else if (ds_type == DS_TYPE_ABSOLUTE)
303     lua_pushinteger (l, (lua_Integer) v.absolute);
304   else
305     return (-1);
306   return (0);
307 } /* }}} int luaC_pushvalue */
308
309 int luaC_pushvaluelist (lua_State *l, const data_set_t *ds, const value_list_t *vl) /* {{{ */
310 {
311   lua_newtable (l);
312
313   lua_pushstring (l, vl->host);
314   lua_setfield (l, /* idx = */ -2, "host");
315
316   lua_pushstring (l, vl->plugin);
317   lua_setfield (l, /* idx = */ -2, "plugin");
318   lua_pushstring (l, vl->plugin_instance);
319   lua_setfield (l, /* idx = */ -2, "plugin_instance");
320
321   lua_pushstring (l, vl->type);
322   lua_setfield (l, /* idx = */ -2, "type");
323   lua_pushstring (l, vl->type_instance);
324   lua_setfield (l, /* idx = */ -2, "type_instance");
325
326   luaC_pushvalues (l, ds, vl);
327   lua_setfield (l, /* idx = */ -2, "values");
328
329   luaC_pushdstypes (l, ds);
330   lua_setfield (l, /* idx = */ -2, "dstypes");
331
332   luaC_pushdsnames (l, ds);
333   lua_setfield (l, /* idx = */ -2, "dsnames");
334
335   luaC_pushcdtime (l, vl->time);
336   lua_setfield (l, /* idx = */ -2, "time");
337
338   luaC_pushcdtime (l, vl->interval);
339   lua_setfield (l, /* idx = */ -2, "interval");
340
341   return (0);
342 } /* }}} int luaC_pushvaluelist */
343
344 /* vim: set sw=2 sts=2 et fdm=marker : */