Move CONTRIBUTING.md to the docs/ directory.
[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 #include "common.h"
28 #include "utils_lua.h"
29
30 static int ltoc_values(lua_State *L, /* {{{ */
31                        const data_set_t *ds, value_t *ret_values) {
32   if (!lua_istable(L, -1)) {
33     WARNING("ltoc_values: not a table");
34     return -1;
35   }
36
37   /* Push initial key */
38   lua_pushnil(L); /* +1 = 1 */
39   size_t i = 0;
40   while (lua_next(L, -2) != 0) /* -1+2 = 2 || -1 = 0 */
41   {
42     if (i >= ds->ds_num) {
43       lua_pop(L, 2); /* -2 = 0 */
44       i++;
45       break;
46     }
47
48     ret_values[i] = luaC_tovalue(L, -1, ds->ds[i].type);
49
50     /* Pop the value */
51     lua_pop(L, 1); /* -1 = 1 */
52     i++;
53   } /* while (lua_next) */
54
55   if (i != ds->ds_num) {
56     WARNING("ltoc_values: invalid size for datasource \"%s\": expected %" PRIsz
57             ", got %" PRIsz,
58             ds->type, ds->ds_num, i);
59     return -1;
60   }
61
62   return 0;
63 } /* }}} int ltoc_values */
64
65 static int ltoc_table_values(lua_State *L, int idx, /* {{{ */
66                              const data_set_t *ds, value_list_t *vl) {
67   /* We're only called from "luaC_tovaluelist", which ensures that "idx" is an
68    * absolute index (i.e. a positive number) */
69   assert(idx > 0);
70
71   lua_getfield(L, idx, "values");
72   if (!lua_istable(L, -1)) {
73     WARNING("utils_lua: ltoc_table_values: The \"values\" member is a %s "
74             "value, not a table.",
75             lua_typename(L, lua_type(L, -1)));
76     lua_pop(L, 1);
77     return -1;
78   }
79
80   vl->values_len = ds->ds_num;
81   vl->values = calloc(vl->values_len, sizeof(*vl->values));
82   if (vl->values == NULL) {
83     ERROR("utils_lua: calloc failed.");
84     vl->values_len = 0;
85     lua_pop(L, 1);
86     return -1;
87   }
88
89   int status = ltoc_values(L, ds, vl->values);
90
91   lua_pop(L, 1);
92
93   if (status != 0) {
94     vl->values_len = 0;
95     sfree(vl->values);
96   }
97
98   return status;
99 } /* }}} int ltoc_table_values */
100
101 static int luaC_pushvalues(lua_State *L, const data_set_t *ds,
102                            const value_list_t *vl) /* {{{ */
103 {
104   assert(vl->values_len == ds->ds_num);
105
106   lua_newtable(L);
107   for (size_t i = 0; i < vl->values_len; i++) {
108     lua_pushinteger(L, (lua_Integer)i + 1);
109     luaC_pushvalue(L, vl->values[i], ds->ds[i].type);
110     lua_settable(L, -3);
111   }
112
113   return 0;
114 } /* }}} int luaC_pushvalues */
115
116 static int luaC_pushdstypes(lua_State *L, const data_set_t *ds) /* {{{ */
117 {
118   lua_newtable(L);
119   for (size_t i = 0; i < ds->ds_num; i++) {
120     lua_pushinteger(L, (lua_Integer)i);
121     lua_pushstring(L, DS_TYPE_TO_STRING(ds->ds[i].type));
122     lua_settable(L, -3);
123   }
124
125   return 0;
126 } /* }}} int luaC_pushdstypes */
127
128 static int luaC_pushdsnames(lua_State *L, const data_set_t *ds) /* {{{ */
129 {
130   lua_newtable(L);
131   for (size_t i = 0; i < ds->ds_num; i++) {
132     lua_pushinteger(L, (lua_Integer)i);
133     lua_pushstring(L, ds->ds[i].name);
134     lua_settable(L, -3);
135   }
136
137   return 0;
138 } /* }}} int luaC_pushdsnames */
139
140 /*
141  * Public functions
142  */
143 cdtime_t luaC_tocdtime(lua_State *L, int idx) /* {{{ */
144 {
145   if (!lua_isnumber(L, /* stack pos = */ idx))
146     return 0;
147
148   double d = lua_tonumber(L, idx);
149
150   return DOUBLE_TO_CDTIME_T(d);
151 } /* }}} int ltoc_table_cdtime */
152
153 int luaC_tostringbuffer(lua_State *L, int idx, /* {{{ */
154                         char *buffer, size_t buffer_size) {
155   const char *str = lua_tostring(L, idx);
156   if (str == NULL)
157     return -1;
158
159   sstrncpy(buffer, str, buffer_size);
160   return 0;
161 } /* }}} int luaC_tostringbuffer */
162
163 value_t luaC_tovalue(lua_State *L, int idx, int ds_type) /* {{{ */
164 {
165   value_t v = {0};
166
167   if (!lua_isnumber(L, idx))
168     return v;
169
170   if (ds_type == DS_TYPE_GAUGE)
171     v.gauge = (gauge_t)lua_tonumber(L, /* stack pos = */ -1);
172   else if (ds_type == DS_TYPE_DERIVE)
173     v.derive = (derive_t)lua_tointeger(L, /* stack pos = */ -1);
174   else if (ds_type == DS_TYPE_COUNTER)
175     v.counter = (counter_t)lua_tointeger(L, /* stack pos = */ -1);
176   else if (ds_type == DS_TYPE_ABSOLUTE)
177     v.absolute = (absolute_t)lua_tointeger(L, /* stack pos = */ -1);
178
179   return v;
180 } /* }}} value_t luaC_tovalue */
181
182 value_list_t *luaC_tovaluelist(lua_State *L, int idx) /* {{{ */
183 {
184 #if COLLECT_DEBUG
185   int stack_top_before = lua_gettop(L);
186 #endif
187
188   /* Convert relative indexes to absolute indexes, so it doesn't change when we
189    * push / pop stuff. */
190   if (idx < 1)
191     idx += lua_gettop(L) + 1;
192
193   /* Check that idx is in the valid range */
194   if ((idx < 1) || (idx > lua_gettop(L))) {
195     DEBUG("luaC_tovaluelist: idx(%d), top(%d)", idx, stack_top_before);
196     return NULL;
197   }
198
199   value_list_t *vl = calloc(1, sizeof(*vl));
200   if (vl == NULL) {
201     DEBUG("luaC_tovaluelist: calloc failed");
202     return NULL;
203   }
204
205   /* Push initial key */
206   lua_pushnil(L);
207   while (lua_next(L, idx) != 0) {
208     const char *key = lua_tostring(L, -2);
209
210     if (key == NULL) {
211       DEBUG("luaC_tovaluelist: Ignoring non-string key.");
212     } else if (strcasecmp("host", key) == 0)
213       luaC_tostringbuffer(L, -1, vl->host, sizeof(vl->host));
214     else if (strcasecmp("plugin", key) == 0)
215       luaC_tostringbuffer(L, -1, vl->plugin, sizeof(vl->plugin));
216     else if (strcasecmp("plugin_instance", key) == 0)
217       luaC_tostringbuffer(L, -1, vl->plugin_instance,
218                           sizeof(vl->plugin_instance));
219     else if (strcasecmp("type", key) == 0)
220       luaC_tostringbuffer(L, -1, vl->type, sizeof(vl->type));
221     else if (strcasecmp("type_instance", key) == 0)
222       luaC_tostringbuffer(L, -1, vl->type_instance, sizeof(vl->type_instance));
223     else if (strcasecmp("time", key) == 0)
224       vl->time = luaC_tocdtime(L, -1);
225     else if (strcasecmp("interval", key) == 0)
226       vl->interval = luaC_tocdtime(L, -1);
227     else if (strcasecmp("values", key) == 0) {
228       /* This key is not handled here, because we have to assure "type" is read
229        * first. */
230     } else {
231       DEBUG("luaC_tovaluelist: Ignoring unknown key \"%s\".", key);
232     }
233
234     /* Pop the value */
235     lua_pop(L, 1);
236   }
237
238   const data_set_t *ds = plugin_get_ds(vl->type);
239   if (ds == NULL) {
240     INFO("utils_lua: Unable to lookup type \"%s\".", vl->type);
241     sfree(vl);
242     return NULL;
243   }
244
245   int status = ltoc_table_values(L, idx, ds, vl);
246   if (status != 0) {
247     WARNING("utils_lua: ltoc_table_values failed.");
248     sfree(vl);
249     return NULL;
250   }
251
252 #if COLLECT_DEBUG
253   assert(stack_top_before == lua_gettop(L));
254 #endif
255   return vl;
256 } /* }}} value_list_t *luaC_tovaluelist */
257
258 int luaC_pushcdtime(lua_State *L, cdtime_t t) /* {{{ */
259 {
260   double d = CDTIME_T_TO_DOUBLE(t);
261
262   lua_pushnumber(L, (lua_Number)d);
263   return 0;
264 } /* }}} int luaC_pushcdtime */
265
266 int luaC_pushvalue(lua_State *L, value_t v, int ds_type) /* {{{ */
267 {
268   if (ds_type == DS_TYPE_GAUGE)
269     lua_pushnumber(L, (lua_Number)v.gauge);
270   else if (ds_type == DS_TYPE_DERIVE)
271     lua_pushinteger(L, (lua_Integer)v.derive);
272   else if (ds_type == DS_TYPE_COUNTER)
273     lua_pushinteger(L, (lua_Integer)v.counter);
274   else if (ds_type == DS_TYPE_ABSOLUTE)
275     lua_pushinteger(L, (lua_Integer)v.absolute);
276   else
277     return -1;
278   return 0;
279 } /* }}} int luaC_pushvalue */
280
281 int luaC_pushvaluelist(lua_State *L, const data_set_t *ds,
282                        const value_list_t *vl) /* {{{ */
283 {
284   lua_newtable(L);
285
286   lua_pushstring(L, vl->host);
287   lua_setfield(L, -2, "host");
288
289   lua_pushstring(L, vl->plugin);
290   lua_setfield(L, -2, "plugin");
291   lua_pushstring(L, vl->plugin_instance);
292   lua_setfield(L, -2, "plugin_instance");
293
294   lua_pushstring(L, vl->type);
295   lua_setfield(L, -2, "type");
296   lua_pushstring(L, vl->type_instance);
297   lua_setfield(L, -2, "type_instance");
298
299   luaC_pushvalues(L, ds, vl);
300   lua_setfield(L, -2, "values");
301
302   luaC_pushdstypes(L, ds);
303   lua_setfield(L, -2, "dstypes");
304
305   luaC_pushdsnames(L, ds);
306   lua_setfield(L, -2, "dsnames");
307
308   luaC_pushcdtime(L, vl->time);
309   lua_setfield(L, -2, "time");
310
311   luaC_pushcdtime(L, vl->interval);
312   lua_setfield(L, -2, "interval");
313
314   return 0;
315 } /* }}} int luaC_pushvaluelist */