Merge remote-tracking branch 'github/pr/2058'
[collectd.git] / src / utils_lua.c
1 /**
2  * collectd - src/utils_lua.c
3  * Copyright (C) 2010       Florian Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  * Authors:
24  *   Florian Forster <octo at collectd.org>
25  **/
26
27 /* <lua5.1/luaconf.h> defines a macro using "sprintf". Although not used here,
28  * GCC will complain about the macro definition. */
29 #define DONT_POISON_SPRINTF_YET
30
31 #include "utils_lua.h"
32 #include "common.h"
33
34 static int ltoc_values(lua_State *L, /* {{{ */
35                        const data_set_t *ds, value_t *ret_values) {
36   if (!lua_istable(L, -1)) {
37     WARNING("ltoc_values: not a table");
38     return (-1);
39   }
40
41   /* Push initial key */
42   lua_pushnil(L); /* +1 = 1 */
43   size_t i = 0;
44   while (lua_next(L, -2) != 0) /* -1+2 = 2 || -1 = 0 */
45   {
46     if (i >= ds->ds_num) {
47       lua_pop(L, 2); /* -2 = 0 */
48       i++;
49       break;
50     }
51
52     ret_values[i] = luaC_tovalue(L, -1, ds->ds[i].type);
53
54     /* Pop the value */
55     lua_pop(L, 1); /* -1 = 1 */
56     i++;
57   } /* while (lua_next) */
58
59   if (i != ds->ds_num) {
60     WARNING("ltoc_values: invalid size for datasource \"%s\": expected %zu, "
61             "got %zu",
62             ds->type, ds->ds_num, i);
63     return (-1);
64   }
65
66   return (0);
67 } /* }}} int ltoc_values */
68
69 static int ltoc_table_values(lua_State *L, int idx, /* {{{ */
70                              const data_set_t *ds, value_list_t *vl) {
71   /* We're only called from "luaC_tovaluelist", which ensures that "idx" is an
72    * absolute index (i.e. a positive number) */
73   assert(idx > 0);
74
75   lua_getfield(L, idx, "values");
76   if (!lua_istable(L, -1)) {
77     WARNING("utils_lua: ltoc_table_values: The \"values\" member is a %s "
78             "value, not a table.",
79             lua_typename(L, lua_type(L, -1)));
80     lua_pop(L, 1);
81     return (-1);
82   }
83
84   vl->values_len = ds->ds_num;
85   vl->values = calloc(vl->values_len, sizeof(*vl->values));
86   if (vl->values == NULL) {
87     ERROR("utils_lua: calloc failed.");
88     vl->values_len = 0;
89     lua_pop(L, 1);
90     return (-1);
91   }
92
93   int status = ltoc_values(L, ds, vl->values);
94
95   lua_pop(L, 1);
96
97   if (status != 0) {
98     vl->values_len = 0;
99     sfree(vl->values);
100   }
101
102   return (status);
103 } /* }}} int ltoc_table_values */
104
105 static int luaC_pushvalues(lua_State *L, const data_set_t *ds,
106                            const value_list_t *vl) /* {{{ */
107 {
108   assert(vl->values_len == ds->ds_num);
109
110   lua_newtable(L);
111   for (size_t i = 0; i < vl->values_len; i++) {
112     lua_pushinteger(L, (lua_Integer)i + 1);
113     luaC_pushvalue(L, vl->values[i], ds->ds[i].type);
114     lua_settable(L, -3);
115   }
116
117   return (0);
118 } /* }}} int luaC_pushvalues */
119
120 static int luaC_pushdstypes(lua_State *L, const data_set_t *ds) /* {{{ */
121 {
122   lua_newtable(L);
123   for (size_t i = 0; i < ds->ds_num; i++) {
124     lua_pushinteger(L, (lua_Integer)i);
125     lua_pushstring(L, DS_TYPE_TO_STRING(ds->ds[i].type));
126     lua_settable(L, -3);
127   }
128
129   return (0);
130 } /* }}} int luaC_pushdstypes */
131
132 static int luaC_pushdsnames(lua_State *L, const data_set_t *ds) /* {{{ */
133 {
134   lua_newtable(L);
135   for (size_t i = 0; i < ds->ds_num; i++) {
136     lua_pushinteger(L, (lua_Integer)i);
137     lua_pushstring(L, ds->ds[i].name);
138     lua_settable(L, -3);
139   }
140
141   return (0);
142 } /* }}} int luaC_pushdsnames */
143
144 /*
145  * Public functions
146  */
147 cdtime_t luaC_tocdtime(lua_State *L, int idx) /* {{{ */
148 {
149   if (!lua_isnumber(L, /* stack pos = */ idx))
150     return (0);
151
152   double d = lua_tonumber(L, idx);
153
154   return (DOUBLE_TO_CDTIME_T(d));
155 } /* }}} int ltoc_table_cdtime */
156
157 int luaC_tostringbuffer(lua_State *L, int idx, /* {{{ */
158                         char *buffer, size_t buffer_size) {
159   const char *str = lua_tostring(L, idx);
160   if (str == NULL)
161     return (-1);
162
163   sstrncpy(buffer, str, buffer_size);
164   return (0);
165 } /* }}} int luaC_tostringbuffer */
166
167 value_t luaC_tovalue(lua_State *L, int idx, int ds_type) /* {{{ */
168 {
169   value_t v = { 0 };
170
171   if (!lua_isnumber(L, idx))
172     return (v);
173
174   if (ds_type == DS_TYPE_GAUGE)
175     v.gauge = (gauge_t)lua_tonumber(L, /* stack pos = */ -1);
176   else if (ds_type == DS_TYPE_DERIVE)
177     v.derive = (derive_t)lua_tointeger(L, /* stack pos = */ -1);
178   else if (ds_type == DS_TYPE_COUNTER)
179     v.counter = (counter_t)lua_tointeger(L, /* stack pos = */ -1);
180   else if (ds_type == DS_TYPE_ABSOLUTE)
181     v.absolute = (absolute_t)lua_tointeger(L, /* stack pos = */ -1);
182
183   return (v);
184 } /* }}} value_t luaC_tovalue */
185
186 value_list_t *luaC_tovaluelist(lua_State *L, int idx) /* {{{ */
187 {
188 #if COLLECT_DEBUG
189   int stack_top_before = lua_gettop(L);
190 #endif
191
192   /* Convert relative indexes to absolute indexes, so it doesn't change when we
193    * push / pop stuff. */
194   if (idx < 1)
195     idx += lua_gettop(L) + 1;
196
197   /* Check that idx is in the valid range */
198   if ((idx < 1) || (idx > lua_gettop(L))) {
199     DEBUG("luaC_tovaluelist: idx(%d), top(%d)", idx, stack_top_before);
200     return (NULL);
201   }
202
203   value_list_t *vl = calloc(1, sizeof(*vl));
204   if (vl == NULL) {
205     DEBUG("luaC_tovaluelist: calloc failed");
206     return (NULL);
207   }
208
209   /* Push initial key */
210   lua_pushnil(L);
211   while (lua_next(L, idx) != 0) {
212     const char *key = lua_tostring(L, -2);
213
214     if (key == NULL) {
215       DEBUG("luaC_tovaluelist: Ignoring non-string key.");
216     } else if (strcasecmp("host", key) == 0)
217       luaC_tostringbuffer(L, -1, vl->host, sizeof(vl->host));
218     else if (strcasecmp("plugin", key) == 0)
219       luaC_tostringbuffer(L, -1, vl->plugin, sizeof(vl->plugin));
220     else if (strcasecmp("plugin_instance", key) == 0)
221       luaC_tostringbuffer(L, -1, vl->plugin_instance,
222                           sizeof(vl->plugin_instance));
223     else if (strcasecmp("type", key) == 0)
224       luaC_tostringbuffer(L, -1, vl->type, sizeof(vl->type));
225     else if (strcasecmp("type_instance", key) == 0)
226       luaC_tostringbuffer(L, -1, vl->type_instance,
227                           sizeof(vl->type_instance));
228     else if (strcasecmp("time", key) == 0)
229       vl->time = luaC_tocdtime(L, -1);
230     else if (strcasecmp("interval", key) == 0)
231       vl->interval = luaC_tocdtime(L, -1);
232     else if (strcasecmp("values", key) == 0) {
233       /* This key is not handled here, because we have to assure "type" is read
234        * first. */
235     } else {
236       DEBUG("luaC_tovaluelist: Ignoring unknown key \"%s\".", key);
237     }
238
239     /* Pop the value */
240     lua_pop(L, 1);
241   }
242
243   const data_set_t *ds = plugin_get_ds(vl->type);
244   if (ds == NULL) {
245     INFO("utils_lua: Unable to lookup type \"%s\".", vl->type);
246     sfree(vl);
247     return (NULL);
248   }
249
250   int status = ltoc_table_values(L, idx, ds, vl);
251   if (status != 0) {
252     WARNING("utils_lua: ltoc_table_values failed.");
253     sfree(vl);
254     return (NULL);
255   }
256
257 #if COLLECT_DEBUG
258   assert(stack_top_before == lua_gettop(L));
259 #endif
260   return (vl);
261 } /* }}} value_list_t *luaC_tovaluelist */
262
263 int luaC_pushcdtime(lua_State *L, cdtime_t t) /* {{{ */
264 {
265   double d = CDTIME_T_TO_DOUBLE(t);
266
267   lua_pushnumber(L, (lua_Number)d);
268   return (0);
269 } /* }}} int luaC_pushcdtime */
270
271 int luaC_pushvalue(lua_State *L, value_t v, int ds_type) /* {{{ */
272 {
273   if (ds_type == DS_TYPE_GAUGE)
274     lua_pushnumber(L, (lua_Number)v.gauge);
275   else if (ds_type == DS_TYPE_DERIVE)
276     lua_pushinteger(L, (lua_Integer)v.derive);
277   else if (ds_type == DS_TYPE_COUNTER)
278     lua_pushinteger(L, (lua_Integer)v.counter);
279   else if (ds_type == DS_TYPE_ABSOLUTE)
280     lua_pushinteger(L, (lua_Integer)v.absolute);
281   else
282     return (-1);
283   return (0);
284 } /* }}} int luaC_pushvalue */
285
286 int luaC_pushvaluelist(lua_State *L, const data_set_t *ds,
287                        const value_list_t *vl) /* {{{ */
288 {
289   lua_newtable(L);
290
291   lua_pushstring(L, vl->host);
292   lua_setfield(L, -2, "host");
293
294   lua_pushstring(L, vl->plugin);
295   lua_setfield(L, -2, "plugin");
296   lua_pushstring(L, vl->plugin_instance);
297   lua_setfield(L, -2, "plugin_instance");
298
299   lua_pushstring(L, vl->type);
300   lua_setfield(L, -2, "type");
301   lua_pushstring(L, vl->type_instance);
302   lua_setfield(L, -2, "type_instance");
303
304   luaC_pushvalues(L, ds, vl);
305   lua_setfield(L, -2, "values");
306
307   luaC_pushdstypes(L, ds);
308   lua_setfield(L, -2, "dstypes");
309
310   luaC_pushdsnames(L, ds);
311   lua_setfield(L, -2, "dsnames");
312
313   luaC_pushcdtime(L, vl->time);
314   lua_setfield(L, -2, "time");
315
316   luaC_pushcdtime(L, vl->interval);
317   lua_setfield(L, -2, "interval");
318
319   return (0);
320 } /* }}} int luaC_pushvaluelist */
321
322 /* vim: set sw=2 sts=2 et fdm=marker : */