1 /*
2  * Lua bindings for RRDtool
3  *
4  * This software is licensed to the public under the Free Software
5  * Foundation's GNU GPL, version 2 or later. You may obtain a copy
6  * of the GPL by visiting the Free Software Foundations web site at
7  * www.fsf.org, and a copy is included in this distribution.
8  *
9  * Copyright 2008 Fidelis Assis, all rights reserved.
10  *
11  */
12 
13 #include <ctype.h>
14 #include <stddef.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <math.h>
20 #include <errno.h>
21 #include <dirent.h>
22 #include <inttypes.h>
23 
24 #include "lua.h"
25 #include "lauxlib.h"
26 #include "lualib.h"
27 #include "../../src/rrd_tool.h"
28 
29 #ifdef LUA50
30 #ifdef HAVE_COMPAT51
31 #include "compat-5.1.h"
32 #else
33 #include "compat-5.1r5/compat-5.1.h"
34 #endif
35 #endif
36 
37 extern void rrd_freemem(void *mem);
38 
39 extern int luaopen_rrd (lua_State * L);
40 typedef int (*RRD_FUNCTION)(int, char **);
41 typedef rrd_info_t *(RRD_FUNCTION_V)(int, char **);
42 
43 /**********************************************************/
44 
reset_rrd_state(void)45 static void reset_rrd_state(void)
46 {
47     optind = 0;
48     opterr = 0;
49     rrd_clear_error();
50 }
51 
make_argv(const char * cmd,lua_State * L)52 static char **make_argv(const char *cmd, lua_State * L)
53 {
54   char **argv;
55   int i;
56   int argc = lua_gettop(L) + 1;
57 
58   if (!(argv = calloc(argc, sizeof (char *))))
59     /* raise an error and never return */
60     luaL_error(L, "Can't allocate memory for arguments array", cmd);
61 
62   /* fprintf(stderr, "Args:\n"); */
63   argv[0] = (char *) cmd; /* Dummy arg. Cast to (char *) because rrd */
64                           /* functions don't expect (const * char)   */
65   /* fprintf(stderr, "%s\n", argv[0]); */
66   for (i=1; i<argc; i++) {
67     /* accepts string or number */
68     if (lua_isstring(L, i) || lua_isnumber(L, i)) {
69       if (!(argv[i] = (char *) lua_tostring (L, i))) {
70         /* raise an error and never return */
71         luaL_error(L, "%s - error duplicating string area for arg #%d",
72                    cmd, i);
73       }
74     } else {
75       /* raise an error and never return */
76       luaL_error(L, "Invalid arg #%d to %s: args must be strings or numbers",
77                  i, cmd);
78     }
79     /* fprintf(stderr, "%s\n", argv[i]); */
80   }
81   return argv;
82 }
83 
84 static int
rrd_common_call(lua_State * L,const char * cmd,RRD_FUNCTION rrd_function)85 rrd_common_call (lua_State *L, const char *cmd, RRD_FUNCTION rrd_function)
86 {
87   char **argv;
88   int argc = lua_gettop(L) + 1;
89 
90   argv = make_argv(cmd, L);
91   reset_rrd_state();
92   rrd_function(argc, argv);
93   free(argv);
94   if (rrd_test_error()) luaL_error(L, rrd_get_error());
95   return 0;
96 }
97 
98 #if defined(DINF)
99 static int
lua_rrd_infocall(lua_State * L,const char * cmd,RRD_FUNCTION_V rrd_function)100 lua_rrd_infocall(lua_State *L, const char *cmd, RRD_FUNCTION_V rrd_function)
101 {
102   char **argv;
103   rrd_info_t *p, *data;
104   int argc = lua_gettop(L) + 1;
105 
106   argv = make_argv(cmd, L);
107   reset_rrd_state();
108   data = rrd_function(argc, argv);
109   free(argv);
110   if (rrd_test_error()) luaL_error(L, rrd_get_error());
111 
112   lua_newtable(L);
113   p = data;
114   while (data) {
115     lua_pushstring(L, data->key);
116     switch (data->type) {
117       case RD_I_CNT:
118         if (isnan(data->value.u_val)) {
119           lua_pushnil(L);
120         } else {
121           lua_pushnumber(L, (lua_Number) data->value.u_val);
122         }
123         lua_rawset(L, -3);
124         break;
125       case RD_I_VAL:
126         lua_pushnumber(L, (lua_Number) data->value.u_val);
127         lua_rawset(L, -3);
128         break;
129       case RD_I_STR:
130         lua_pushstring(L, data->value.u_str);
131         lua_rawset(L, -3);
132         break;
133       case RD_I_BLO:
134         lua_pushlstring(L, (const char *) data->value.u_blo.ptr,
135                         data->value.u_blo.size);
136         lua_rawset(L, -3);
137         break;
138       default:
139         rrd_info_free(p);
140         return luaL_error(L, "Wrong data type to info call");
141         break;
142     }
143     data = data->next;
144   }
145   rrd_info_free(p);
146   return 1;
147 }
148 #endif
149 
150 /**********************************************************/
151 
152 static int
lua_rrd_create(lua_State * L)153 lua_rrd_create (lua_State * L)
154 {
155   rrd_common_call(L, "create", rrd_create);
156   return 0;
157 }
158 
159 static int
lua_rrd_dump(lua_State * L)160 lua_rrd_dump (lua_State * L)
161 {
162   rrd_common_call(L, "dump", rrd_dump);
163   return 0;
164 }
165 
166 static int
lua_rrd_resize(lua_State * L)167 lua_rrd_resize (lua_State * L)
168 {
169   rrd_common_call(L, "resize", rrd_resize);
170   return 0;
171 }
172 
173 #ifdef HAVE_RRD_RESTORE
174 static int
lua_rrd_restore(lua_State * L)175 lua_rrd_restore (lua_State * L)
176 {
177   rrd_common_call(L, "restore", rrd_restore);
178   return 0;
179 }
180 #endif
181 
182 static int
lua_rrd_tune(lua_State * L)183 lua_rrd_tune (lua_State * L)
184 {
185   rrd_common_call(L, "tune", rrd_tune);
186   return 0;
187 }
188 
189 static int
lua_rrd_update(lua_State * L)190 lua_rrd_update (lua_State * L)
191 {
192   rrd_common_call(L, "update", rrd_update);
193   return 0;
194 }
195 
196 static int
lua_rrd_fetch(lua_State * L)197 lua_rrd_fetch (lua_State * L)
198 {
199   int argc = lua_gettop(L) + 1;
200   char **argv = make_argv("fetch", L);
201   unsigned long i, j, step, ds_cnt;
202   rrd_value_t *data, *p;
203   char    **names;
204   time_t  t, start, end;
205 
206   reset_rrd_state();
207   rrd_fetch(argc, argv, &start, &end, &step, &ds_cnt, &names, &data);
208   free(argv);
209   if (rrd_test_error()) luaL_error(L, rrd_get_error());
210 
211   lua_pushnumber(L, (lua_Number) start);
212   lua_pushnumber(L, (lua_Number) step);
213   /* fprintf(stderr, "%lu, %lu, %lu, %lu\n", start, end, step, num_points); */
214 
215   /* create the ds names array */
216   lua_newtable(L);
217   for (i=0; i<ds_cnt; i++) {
218     lua_pushstring(L, names[i]);
219     lua_rawseti(L, -2, i+1);
220     rrd_freemem(names[i]);
221   }
222   rrd_freemem(names);
223 
224   /* create the data points array */
225   lua_newtable(L);
226   p = data;
227   for (t=start, i=0; t<end; t+=step, i++) {
228     lua_newtable(L);
229     for (j=0; j<ds_cnt; j++) {
230       /*fprintf(stderr, "Point #%lu\n", j+1); */
231       lua_pushnumber(L, (lua_Number) *p++);
232       lua_rawseti(L, -2, j+1);
233     }
234     lua_rawseti(L, -2, i+1);
235   }
236   rrd_freemem(data);
237 
238   /* return the end as the last value */
239   lua_pushnumber(L, (lua_Number) end);
240 
241   return 5;
242 }
243 
244 static int
lua_rrd_first(lua_State * L)245 lua_rrd_first (lua_State * L)
246 {
247   time_t first;
248   int argc = lua_gettop(L) + 1;
249   char **argv = make_argv("first", L);
250   reset_rrd_state();
251   first = rrd_first(argc, argv);
252   free(argv);
253   if (rrd_test_error()) luaL_error(L, rrd_get_error());
254   lua_pushnumber(L, (lua_Number) first);
255   return 1;
256 }
257 
258 static int
lua_rrd_last(lua_State * L)259 lua_rrd_last (lua_State * L)
260 {
261   time_t last;
262   int argc = lua_gettop(L) + 1;
263   char **argv = make_argv("last", L);
264   reset_rrd_state();
265   last = rrd_last(argc, argv);
266   free(argv);
267   if (rrd_test_error()) luaL_error(L, rrd_get_error());
268   lua_pushnumber(L, (lua_Number) last);
269   return 1;
270 }
271 
272 #ifdef HAVE_RRD_GRAPH
273 
274 static int
lua_rrd_graph(lua_State * L)275 lua_rrd_graph (lua_State * L)
276 {
277   int argc = lua_gettop(L) + 1;
278   char **argv = make_argv("last", L);
279   char **calcpr;
280   int i, xsize, ysize;
281   double ymin, ymax;
282 
283   reset_rrd_state();
284   rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
285   free(argv);
286   if (rrd_test_error()) luaL_error(L, rrd_get_error());
287   lua_pushnumber(L, (lua_Number) xsize);
288   lua_pushnumber(L, (lua_Number) ysize);
289   lua_newtable(L);
290   for (i = 0; calcpr && calcpr[i]; i++) {
291       lua_pushstring(L, calcpr[i]);
292       lua_rawseti(L, -2, i+1);
293       rrd_freemem(calcpr[i]);
294   }
295   rrd_freemem(calcpr);
296   return 3;
297 }
298 
299 #endif
300 
301 static int
lua_rrd_flushcached(lua_State * L)302 lua_rrd_flushcached(lua_State *L)
303 {
304   return rrd_common_call(L, "flushcached", rrd_flushcached);
305 }
306 
307 #if defined(DINF)
308 static int
lua_rrd_info(lua_State * L)309 lua_rrd_info (lua_State * L)
310 {
311   return lua_rrd_infocall(L, "info", rrd_info);
312 }
313 
314 #ifdef HAVE_RRD_GRAPH
315 
316 static int
lua_rrd_graphv(lua_State * L)317 lua_rrd_graphv (lua_State * L)
318 {
319   return lua_rrd_infocall(L, "graphv", rrd_graph_v);
320 }
321 
322 #endif
323 
324 static int
lua_rrd_updatev(lua_State * L)325 lua_rrd_updatev (lua_State * L)
326 {
327   return lua_rrd_infocall(L, "updatev", rrd_update_v);
328 }
329 #endif
330 
331 /**********************************************************/
332 
333 /*
334 ** Assumes the table is on top of the stack.
335 */
336 static void
set_info(lua_State * L)337 set_info (lua_State * L)
338 {
339   lua_pushliteral (L, "_COPYRIGHT");
340   lua_pushliteral (L, "Copyright (C) 2008 Fidelis Assis");
341   lua_settable (L, -3);
342   lua_pushliteral (L, "_DESCRIPTION");
343   lua_pushliteral (L, "RRD-lua is a Lua binding for RRDtool.");
344   lua_settable (L, -3);
345   lua_pushliteral (L, "_NAME");
346   lua_pushliteral (L, "RRD-Lua");
347   lua_settable (L, -3);
348   lua_pushliteral (L, "_VERSION");
349   lua_pushliteral (L, LIB_VERSION);
350   lua_settable (L, -3);
351 }
352 
353 /**********************************************************/
354 
355 #if defined LUA50
356 static const struct luaL_reg rrd[] = {
357 #elif LUA_VERSION_NUM == 501
358 static const struct luaL_Reg rrd[] = {
359 #else
360 static const struct luaL_Reg rrd[] = {
361 #endif
362   {"create", lua_rrd_create},
363   {"dump", lua_rrd_dump},
364   {"fetch", lua_rrd_fetch},
365   {"first", lua_rrd_first},
366 #ifdef HAVE_RRD_GRAPH
367   {"graph", lua_rrd_graph},
368 #endif
369   {"last", lua_rrd_last},
370   {"resize", lua_rrd_resize},
371 #ifdef HAVE_RRD_RESTORE
372   {"restore", lua_rrd_restore},
373 #endif
374   {"tune", lua_rrd_tune},
375   {"update", lua_rrd_update},
376   {"flushcached", lua_rrd_flushcached},
377 #if defined(DINF)
378   {"info", lua_rrd_info},
379   {"updatev", lua_rrd_updatev},
380 #ifdef HAVE_RRD_GRAPH
381   {"graphv", lua_rrd_graphv},
382 #endif
383 #endif
384   {NULL, NULL}
385 };
386 
387 
388 /*
389 ** Open RRD library
390 */
391 int
luaopen_rrd(lua_State * L)392 luaopen_rrd (lua_State * L)
393 {
394 #if defined LUA50
395   /* luaL_module is defined in compat-5.1.c */
396   luaL_module (L, "rrd", rrd, 0);
397 #elif LUA_VERSION_NUM == 501
398   /* version 5.1 */
399   luaL_register (L, "rrd", rrd);
400 #else
401   luaL_newlib(L, rrd);
402 #endif
403   set_info (L);
404   return 1;
405 }
406