2 * tclrrd.c -- A TCL interpreter extension to access the RRD library.
4 * Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig.
6 * See the file "COPYING" for information on usage and redistribution
7 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17 #include <rrd_format.h>
19 extern int Tclrrd_Init(Tcl_Interp *interp);
20 extern int Tclrrd_SafeInit(Tcl_Interp *interp);
24 * some rrd_XXX() functions might modify the argv strings passed to it.
25 * Hence, we need to do some preparation before
26 * calling the rrd library functions.
28 static char ** getopt_init(int argc, CONST84 char *argv[])
33 argv2 = calloc(argc, sizeof(char *));
34 for (i = 0; i < argc; i++) {
35 argv2[i] = strdup(argv[i]);
40 static void getopt_cleanup(int argc, char **argv2)
44 for (i = 0; i < argc; i++) {
53 Rrd_Create(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
57 argv2 = getopt_init(argc, argv);
58 rrd_create(argc, argv2);
59 getopt_cleanup(argc, argv2);
61 if (rrd_test_error()) {
62 Tcl_AppendResult(interp, "RRD Error: ",
63 rrd_get_error(), (char *) NULL);
74 Rrd_Dump(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
78 argv2 = getopt_init(argc, argv);
79 rrd_dump(argc, argv2);
80 getopt_cleanup(argc, argv2);
82 /* NOTE: rrd_dump() writes to stdout. No interaction with TCL. */
84 if (rrd_test_error()) {
85 Tcl_AppendResult(interp, "RRD Error: ",
86 rrd_get_error(), (char *) NULL);
97 Rrd_Last(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
102 argv2 = getopt_init(argc, argv);
103 t = rrd_last(argc, argv2);
104 getopt_cleanup(argc, argv2);
107 if (rrd_test_error()) {
108 Tcl_AppendResult(interp, "RRD Error: ",
109 rrd_get_error(), (char *) NULL);
114 Tcl_SetIntObj(Tcl_GetObjResult(interp), t);
122 Rrd_Update(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
126 argv2 = getopt_init(argc, argv);
127 rrd_update(argc, argv2);
128 getopt_cleanup(argc, argv2);
130 if (rrd_test_error()) {
131 Tcl_AppendResult(interp, "RRD Error: ",
132 rrd_get_error(), (char *) NULL);
143 Rrd_Fetch(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
145 time_t start, end, j;
146 unsigned long step, ds_cnt, i, ii;
147 rrd_value_t *data, *datai;
153 argv2 = getopt_init(argc, argv);
154 if (rrd_fetch(argc, argv2, &start, &end, &step,
155 &ds_cnt, &ds_namv, &data) != -1) {
157 listPtr = Tcl_GetObjResult(interp);
158 for (j = start; j <= end; j += step) {
159 for (ii = 0; ii < ds_cnt; ii++) {
160 sprintf(s, "%.2f", *(datai++));
161 Tcl_ListObjAppendElement(interp, listPtr,
162 Tcl_NewStringObj(s, -1));
165 for (i=0; i<ds_cnt; i++) free(ds_namv[i]);
169 getopt_cleanup(argc, argv2);
171 if (rrd_test_error()) {
172 Tcl_AppendResult(interp, "RRD Error: ",
173 rrd_get_error(), (char *) NULL);
184 Rrd_Graph(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
194 argv2 = getopt_init(argc, argv);
195 if (rrd_graph(argc, argv2, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) != -1 ) {
196 sprintf(dimensions, "%d %d", xsize, ysize);
197 Tcl_AppendResult(interp, dimensions, (char *) NULL);
202 for(i = 0; calcpr[i]; i++){
203 printf("%s\n", calcpr[i]);
210 getopt_cleanup(argc, argv2);
212 if (rrd_test_error()) {
213 Tcl_AppendResult(interp, "RRD Error: ",
214 rrd_get_error(), (char *) NULL);
225 Rrd_Tune(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
229 argv2 = getopt_init(argc, argv);
230 rrd_tune(argc, argv2);
231 getopt_cleanup(argc, argv2);
233 if (rrd_test_error()) {
234 Tcl_AppendResult(interp, "RRD Error: ",
235 rrd_get_error(), (char *) NULL);
246 Rrd_Resize(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
250 argv2 = getopt_init(argc, argv);
251 rrd_resize(argc, argv2);
252 getopt_cleanup(argc, argv2);
254 if (rrd_test_error()) {
255 Tcl_AppendResult(interp, "RRD Error: ",
256 rrd_get_error(), (char *) NULL);
267 Rrd_Restore(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
271 argv2 = getopt_init(argc, argv);
272 rrd_restore(argc, argv2);
273 getopt_cleanup(argc, argv2);
275 if (rrd_test_error()) {
276 Tcl_AppendResult(interp, "RRD Error: ",
277 rrd_get_error(), (char *) NULL);
288 * The following structure defines the commands in the Rrd extension.
292 char *name; /* Name of the command. */
293 Tcl_CmdProc *proc; /* Procedure for command. */
294 int hide; /* Hide if safe interpreter */
297 static CmdInfo rrdCmds[] = {
298 { "Rrd::create", Rrd_Create, 1 },
299 { "Rrd::dump", Rrd_Dump, 0 },
300 { "Rrd::last", Rrd_Last, 0 },
301 { "Rrd::update", Rrd_Update, 1 },
302 { "Rrd::fetch", Rrd_Fetch, 0 },
303 { "Rrd::graph", Rrd_Graph, 1 }, /* Due to RRD's API, a safe
304 interpreter cannot create
305 a graph since it writes to
306 a filename supplied by the
308 { "Rrd::tune", Rrd_Tune, 1 },
309 { "Rrd::resize", Rrd_Resize, 1 },
310 { "Rrd::restore", Rrd_Restore, 1 },
311 { (char *) NULL, (Tcl_CmdProc *) NULL, 0 }
317 init(Tcl_Interp *interp, int safe)
322 if ( Tcl_InitStubs(interp,TCL_VERSION,0) == NULL )
325 if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) {
330 * Why a global array? In keeping with the Rrd:: namespace, why
331 * not simply create a normal variable Rrd::version and set it?
333 Tcl_SetVar2(interp, "rrd", "version", VERSION, TCL_GLOBAL_ONLY);
335 for (cmdInfoPtr = rrdCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) {
337 * Check if the command already exists and return an error
338 * to ensure we detect name clashes while loading the Rrd
341 if (Tcl_GetCommandInfo(interp, cmdInfoPtr->name, &info)) {
342 Tcl_AppendResult(interp, "command \"", cmdInfoPtr->name,
343 "\" already exists", (char *) NULL);
346 if (safe && cmdInfoPtr->hide) {
349 * Turns out the one cannot hide a command in a namespace
350 * due to a limitation of Tcl, one can only hide global
351 * commands. Thus, if we created the commands without
352 * the Rrd:: namespace in a safe interpreter, then the
353 * "unsafe" commands could be hidden -- which would allow
354 * an owning interpreter either un-hiding them or doing
355 * an "interp invokehidden". If the Rrd:: namespace is
356 * used, then it's still possible for the owning interpreter
357 * to fake out the missing commands:
359 * # Make all Rrd::* commands available in master interperter
360 * package require Rrd
361 * set safe [interp create -safe]
362 * # Make safe Rrd::* commands available in safe interperter
363 * interp invokehidden $safe -global load ./tclrrd1.2.11.so
364 * # Provide the safe interpreter with the missing commands
365 * $safe alias Rrd::update do_update $safe
366 * proc do_update {which_interp $args} {
367 * # Do some checking maybe...
369 * return [eval Rrd::update $args]
372 * Our solution for now is to just not create the "unsafe"
373 * commands in a safe interpreter.
375 if (Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name) != TCL_OK)
380 Tcl_CreateCommand(interp, cmdInfoPtr->name, cmdInfoPtr->proc,
381 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
384 if (Tcl_PkgProvide(interp, "Rrd", VERSION) != TCL_OK) {
392 Tclrrd_Init(Tcl_Interp *interp)
394 return init(interp, 0);
398 * See the comments above and note how few commands are considered "safe"...
399 * Using rrdtool in a safe interpreter has very limited functionality. It's
400 * tempting to just return TCL_ERROR and forget about it.
403 Tclrrd_SafeInit(Tcl_Interp *interp)
405 return init(interp, 1);