fix some string marshaling bugs in the Dot Net binding -- Chris Larsen
[rrdtool.git] / bindings / dotnet / rrdlib.cs
1 /*****************************************************************************
2  * RRDLIB .NET Binding
3  *****************************************************************************
4  * Created 2010/06/29 by Chris Larsen
5  * 
6  * This .NET interface allows the use of Tobias Oetiker's awesome RRDtool 
7  * functions in .NET projects using the PInvoke method to load the rrdlib.dll
8  * To use, please make sure that you place the rrdlib.dll in the same 
9  * directory as this dll, or change the "const string dll" to point to the
10  * proper location. For documentation, please see the RRDtool website at:
11  * http://oss.oetiker.ch/rrdtool/
12  * For useage examples, please see the rrd_binding_test project.
13  ****************************************************************************/
14 using System;
15 using System.Collections.Generic;
16 using System.Runtime.InteropServices;
17
18 /// <summary>
19 /// Contains data structures and methods for working with round robin databases.
20 /// </summary>
21 namespace dnrrdlib
22 {
23     /// <summary>
24     /// Information about a particular RRD parameter. The key is the name of the parameter,
25     /// type determines what kind of value we have, value is the value, and next is a 
26     /// pointer to another info object.
27     /// </summary>
28     [StructLayout(LayoutKind.Explicit, Pack = 1)]
29     public struct rrd_info_t
30     {
31         [FieldOffset(0), MarshalAs(UnmanagedType.LPStr)]
32         public string key;
33         [FieldOffset(4)]    // for 64 bit, set this to 8 and increment everyone else by 4
34         public rrd_info_type_t type;
35         [FieldOffset(8)]
36         public rrd_infoval_t value;
37         [FieldOffset(16)]
38         public IntPtr next;
39     }
40
41     /// <summary>
42     /// This is a chunk of data returned from an RRD object
43     /// </summary>
44     [StructLayout(LayoutKind.Sequential)]
45     public struct rrd_blob_t
46     {
47         public UInt32 size;     /* size of the blob */
48         public IntPtr ptr;    /* pointer */
49     };
50
51     /// <summary>
52     /// This contains the actual data values for an rrd_info_t structure. 
53     /// NOTE: Only one of these will be valid per instance. Use the containing info_t's
54     /// type field to deteremine which of these to read. 
55     /// NOTE: If the type is RD_I_STR, you have to marshal the string value yourself
56     /// </summary>
57     [StructLayout(LayoutKind.Explicit)]
58     public struct rrd_infoval_t
59     {
60         [FieldOffset(0)]
61         public UInt32 u_cnt;
62         [FieldOffset(0)]
63         public double u_val;
64         [FieldOffset(0)]
65         public IntPtr u_str;
66         [FieldOffset(0)]
67         public Int32 u_int;
68         [FieldOffset(0)]
69         public rrd_blob_t u_blo;
70     };
71
72     /// <summary>
73     /// Different rrd_info_t value types
74     /// </summary>
75     public enum rrd_info_type_t
76     {
77         RD_I_VAL = 0,
78         RD_I_CNT,
79         RD_I_STR,
80         RD_I_INT,
81         RD_I_BLO
82     };
83
84     /// <summary>
85     /// Direct bindings to the RRD Library for .NET applications. Uses the PInvoke method
86     /// of accessing the rrdlib.dll file.
87     /// </summary>
88     public class rrd
89     {
90         // Set this path to the location of your "rrdlib.dll" file
91         const string dll = @"C:\Programming\RRDTool\SVN\win32\DebugDLL\rrdlib.dll";
92
93         // IMPORTS - Main methods
94         [DllImport(dll)] static extern Int32 rrd_create(Int32 argc, string[] argv);
95         [DllImport(dll)] static extern Int32 rrd_create_r([MarshalAs(UnmanagedType.LPStr)] string filename, 
96             UInt32 pdp_step, Int32 last_up, Int32 argc, [MarshalAs(UnmanagedType.LPArray)] string[] argv);
97         [DllImport(dll)] static extern IntPtr rrd_info_r(string filename);
98         [DllImport(dll)] static extern void rrd_info_print(IntPtr data);
99         [DllImport(dll)] static extern Int32 rrd_update(Int32 argc, string[] argv);
100         [DllImport(dll)] static extern IntPtr rrd_update_v(Int32 argc, string[] argv);
101         [DllImport(dll)] static extern Int32 rrd_update_r(string filename, string template, Int32 argc,
102             string[] argv);
103         /* Do not use this until someone adds the FILE structure */
104         [DllImport(dll)] static extern Int32 rrd_graph(Int32 argc, string[] argv, ref string[] prdata,
105             ref Int32 xsize, ref Int32 ysize, /* TODO - FILE, */ ref double ymin, ref double ymax);
106         [DllImport(dll)] static extern Int32 rrd_graph_v(Int32 argc, string[] argv);
107         [DllImport(dll)] static extern Int32 rrd_fetch(Int32 argc, string[] argv, ref Int32 start,
108             ref Int32 end, ref UInt32 step, [Out] out UInt32 ds_cnt, [Out] out IntPtr ds_namv, [Out] out IntPtr data);
109         [DllImport(dll)] static extern Int32 rrd_first(Int32 argc, string[] argv);
110         [DllImport(dll)] static extern Int32 rrd_first_r(string filename, Int32 rraindex);
111         [DllImport(dll)] static extern Int32 rrd_last(Int32 argc, string[] argv);
112         [DllImport(dll)] static extern Int32 rrd_last_r(string filename, Int32 rraindex);
113         [DllImport(dll)] static extern Int32 rrd_lastupdate(Int32 argc, string[] argv);
114         [DllImport(dll)] static extern Int32 rrd_lastupdate_r(string filename, ref Int32 ret_last_update,
115             ref UInt32 ret_ds_count, [Out] out IntPtr ret_ds_names, [Out] out IntPtr ret_last_ds);
116         [DllImport(dll)] static extern Int32 rrd_dump(Int32 argc, string[] argv);
117         [DllImport(dll)] static extern Int32 rrd_dump_r(string filename, string outname);
118         [DllImport(dll)] static extern Int32 rrd_xport(Int32 argc, string[] argv, Int32 unused,
119             ref Int32 start, ref Int32 end, ref UInt32 step, ref UInt32 col_cnt,
120             [Out] out IntPtr leggend_v, [Out] out  IntPtr data);
121         [DllImport(dll)] static extern Int32 rrd_restore(Int32 argc, string[] argv);
122         [DllImport(dll)] static extern Int32 rrd_resize(Int32 argc, string[] argv);
123         [DllImport(dll)] static extern Int32 rrd_tune(Int32 argc, string[] argv);
124
125         // IMPORTS - Utility methods
126         [DllImport(dll)] static extern string rrd_strversion();
127         [DllImport(dll)] static extern Int32 rrd_random();
128         [DllImport(dll)] static extern IntPtr rrd_get_error();
129
130         // MAIN FUNCTIONS
131
132         /// <summary>
133         /// The create function of RRDtool lets you set up new Round Robin Database (RRD) files. 
134         /// The file is created at its final, full size and filled with *UNKNOWN* data.
135         /// </summary>
136         /// <param name="argv">String array of command line arguments</param>
137         /// <returns>0 if successful, -1 if an error occurred</returns>
138         public static int Create(string[] argv)
139         {
140             return rrd_create(argv.GetUpperBound(0) + 1, argv);
141         }
142
143         /// <summary>
144         /// The create function of RRDtool lets you set up new Round Robin Database (RRD) files. 
145         /// The file is created at its final, full size and filled with *UNKNOWN* data.
146         /// </summary>
147         /// <param name="filename">A full path to the location where you want the rrd to reside</param>
148         /// <param name="pdp_step">Specifies the base interval in seconds with which data will be fed into the RRD</param>
149         /// <param name="last_up">Timestamp of the last update</param>
150         /// <param name="argv">String array of command line arguments</param>
151         /// <returns>0 if successful, -1 if an error occurred</returns>
152         public static int Create(string filename, UInt32 pdp_step, Int32 last_up, string[] argv)
153         {
154             return rrd_create_r(filename, pdp_step, last_up, argv.GetUpperBound(0)+1, argv);
155         }
156
157         /// <summary>
158         /// Returns a linked list of rrd_info_t objects that describe the rrd file. 
159         /// </summary>
160         /// <param name="filename">Full path to the rrd file</param>
161         /// <returns>An rrd_info_t object</returns>
162         public static rrd_info_t Info(string filename)
163         {
164             if (filename.Length < 1)
165                 throw new Exception("Empty filename");
166             IntPtr ptr = rrd_info_r(filename);
167             if (ptr == IntPtr.Zero || (int)ptr < 1)
168                 throw new Exception("Unable to extract information from rrd");
169             return (rrd_info_t)Marshal.PtrToStructure(ptr, typeof(rrd_info_t));
170         }
171
172         /// <summary>
173         /// The update function feeds new data values into an RRD. The data is time aligned (interpolated) 
174         /// according to the properties of the RRD to which the data is written.
175         /// </summary>
176         /// <param name="argv">String array of command line arguments</param>
177         /// <returns>0 if successful, -1 if an error occurred</returns>
178         public static Int32 Update(string[] argv)
179         {
180             return rrd_update(argv.GetUpperBound(0) + 1, argv);
181         }
182
183         /// <summary>
184         /// The update function feeds new data values into an RRD. The data is time aligned (interpolated) 
185         /// according to the properties of the RRD to which the data is written.
186         /// </summary>
187         /// <param name="argv">String array of command line arguments</param>
188         /// <returns>An rrd_info_t pointer with information about the update</returns>
189         public static IntPtr Update2(string[] argv)
190         {
191             return rrd_update_v(argv.GetUpperBound(0) + 1, argv);
192         }
193
194         /// <summary>
195         /// The update function feeds new data values into an RRD. The data is time aligned (interpolated) 
196         /// according to the properties of the RRD to which the data is written.
197         /// </summary>
198         /// <param name="filename">Full path to the rrd to update</param>
199         /// <param name="template">List of data sources to update and in which order</param>
200         /// <param name="argv">String array of command line arguments</param>
201         /// <returns>0 if successful, -1 if an error occurred</returns>
202         public static Int32 Update(string filename, string template, string[] argv)
203         {
204             return rrd_update_r(filename, template, argv.GetUpperBound(0)+1, argv);
205         }
206
207         /// <summary>
208         /// Generate a graph from an RRD file. Specify all the graph options in the string array as you
209         /// normally would with the command line version.
210         /// </summary>
211         /// <param name="argv">String array of command line arguments</param>
212         /// <returns>0 if successful, -1 if an error occurred</returns>
213         public static Int32 Graph(string[] argv)
214         {
215             return rrd_graph_v(argv.GetUpperBound(0) + 1, argv);
216         }
217
218         /// <summary>
219         /// Returns an array of values for the period specified from a given rrd.
220         /// Specify your parameters in the argv array and check the referenced parameters for
221         /// values returned from the rrd
222         /// </summary>
223         /// <param name="argv">String array of command line arguments (must include the filename)</param>
224         /// <param name="start">Starting timestamp found in the rrd</param>
225         /// <param name="end">Ending timestamp found in the rrd</param>
226         /// <param name="step">The rrd's step value</param>
227         /// <param name="ds_cnt">Number of data sources found</param>
228         /// <param name="ds_namv">Names of data sources found</param>
229         /// <param name="data">Values found (in double type)</param>
230         /// <returns>0 if successful, -1 if an error occurred</returns>
231         public static Int32 Fetch(string[] argv, ref Int32 start, ref Int32 end, ref UInt32 step,
232             ref UInt32 ds_cnt, ref string[] ds_namv, ref IntPtr data)
233         {
234             IntPtr ptr = new IntPtr();
235             Int32 rv = rrd_fetch(argv.GetUpperBound(0) + 1, argv, ref start, ref end, ref step, out ds_cnt,
236                 out ptr, out data);
237             ds_namv = GetStringArray(ptr, ds_cnt);
238             return rv;
239         }
240
241         /// <summary>
242         /// Returns the timestamp of the first value in the rrd given the rra index 
243         /// </summary>
244         /// <param name="filename">Full path to the rrd file</param>
245         /// <param name="rraindex">0 based index of the rra to get a value for</param>
246         /// <returns>Unix timestamp if successful, -1 if an error occurred</returns>
247         public static Int32 First(string filename, int rraindex)
248         {
249             return rrd_first_r(filename, rraindex);
250         }
251
252         /// <summary>
253         /// Returns the timestamp of the first value in the rrd
254         /// </summary>
255         /// <param name="argv">String array of command line arguments</param>
256         /// <returns>Unix timestamp if successful, -1 if an error occurred</returns>
257         public static Int32 First(string[] argv)
258         {
259             return rrd_first(argv.GetUpperBound(0) + 1, argv);
260         }
261
262         /// <summary>
263         /// Returns the timestamp of the last value in the rrd given the rra index
264         /// </summary>
265         /// <param name="filename"></param>
266         /// <param name="filename">Full path to the rrd file</param>
267         /// <param name="rraindex">0 based index of the rra to get a value for</param>
268         /// <returns>Unix timestamp if successful, -1 if an error occurred</returns>
269         public static Int32 Last(string filename, int rraindex)
270         {
271             return rrd_last_r(filename, rraindex);
272         }
273
274         /// <summary>
275         /// Returns the timestamp of the last value in the rrd
276         /// </summary>
277         /// <param name="argv">String array of command line arguments</param>
278         /// <returns>Unix timestamp if successful, -1 if an error occurred</returns>
279         public static Int32 Last(string[] argv)
280         {
281             return rrd_last(argv.GetUpperBound(0) + 1, argv);
282         }
283
284         /// <summary>
285         /// Finds the timestamp of the last updated value in the rrd
286         /// </summary>
287         /// <param name="filename">Full path to the rrd file</param>
288         /// <param name="ret_last_update">Unix timestamp of the last update</param>
289         /// <param name="ret_ds_count">Number of data sources found</param>
290         /// <param name="ret_ds_names">Names of the data sources found</param>
291         /// <param name="ret_last_ds">Name of the last data source found</param>
292         /// <returns>0 if successful, -1 if an error occurred</returns>
293         public static Int32 Last_Update(string filename, ref Int32 ret_last_update, ref UInt32 ret_ds_count,
294             ref string[] ret_ds_names, ref string[] ret_last_ds)
295         {
296             IntPtr ds_names = new IntPtr();
297             IntPtr last_ds = new IntPtr();
298             Int32 rt = rrd_lastupdate_r(filename, ref ret_last_update, ref ret_ds_count, out ds_names,out last_ds);
299             ret_ds_names = GetStringArray(ds_names, ret_ds_count);
300             ret_last_ds = GetStringArray(last_ds, 1);
301             return rt;
302         }
303
304         /// <summary>
305         /// Writes the contents of an rrd file to an XML file
306         /// </summary>
307         /// <param name="filename">Full path to the rrd file</param>
308         /// <param name="outname">Full path to write the XML output</param>
309         /// <returns>0 if successful, -1 if an error occurred</returns>
310         public static Int32 Dump(string filename, string outname)
311         {
312             return rrd_dump_r(filename, outname);
313         }
314
315         /// <summary>
316         /// Writes the contents of an rrd file to an XML file
317         /// </summary>
318         /// <param name="argv">String array of command line arguments</param>
319         /// <returns>0 if successful, -1 if an error occurred</returns>
320         public static Int32 Dump(string[] argv)
321         {
322             return rrd_dump(argv.GetUpperBound(0) + 1, argv);
323         }
324
325         /// <summary>
326         /// Grabs the values from an rrd. Similar to fetch but enables merging of multiple
327         /// rrds and calculations
328         /// </summary>
329         /// <param name="argv">String array of command line arguments</param>
330         /// <param name="start">Starting timestamp found in the rrd</param>
331         /// <param name="end">Ending timestamp found in the rrd</param>
332         /// <param name="step">Step size found in the rrd</param>
333         /// <param name="col_cnt">Number of data sources found in the rrd</param>
334         /// <param name="leggend_v">Add a legend</param>
335         /// <param name="data">Values from the rrd as double type</param>
336         /// <returns>0 if successful, -1 if an error occurred</returns>
337         public static Int32 Xport(string[] argv, ref Int32 start, ref Int32 end, ref UInt32 step,
338             ref UInt32 col_cnt, ref string[] legend_v, ref IntPtr data)
339         {
340             IntPtr legend = new IntPtr();
341             Int32 rt = rrd_xport(argv.GetUpperBound(0) + 1, argv, 0, ref start, ref end, ref step, ref col_cnt,
342                 out legend, out data);
343             legend_v = GetStringArray(legend, col_cnt);
344             return rt;
345         }
346
347         /// <summary>
348         /// Creates an rrd from an XML data dump
349         /// </summary>
350         /// <param name="argv">String array of command line arguments</param>
351         /// <returns>0 if successful, -1 if an error occurred</returns>
352         public static Int32 Restore(string[] argv)
353         {
354             return rrd_restore(argv.GetUpperBound(0) + 1, argv);
355         }
356
357         /// <summary>
358         /// Alters the size of an RRA and creates a new rrd in the dll's directory
359         /// NOTE: The new rrd may return unexpected results if you are not very careful
360         /// NOTE: This may crash in version 1.4.3
361         /// </summary>
362         /// <param name="argv">String array of command line arguments</param>
363         /// <returns>0 if successful, -1 if an error occurred</returns>
364         public static Int32 Resize(string[] argv)
365         {
366             return rrd_resize(argv.GetUpperBound(0) + 1, argv);
367         }
368
369         /// <summary>
370         /// Modify the characteristics of an rrd
371         /// </summary>
372         /// <param name="argv">String array of command line arguments</param>
373         /// <returns>0 if successful, -1 if an error occurred</returns>
374         public static Int32 Tune(string[] argv)
375         {
376             return rrd_tune(argv.GetUpperBound(0) + 1, argv);
377         }
378
379         // UTILITIES
380         /// <summary>
381         /// Returns a string with the numeric version of the rrdlib build version
382         /// </summary>
383         /// <returns>A string with version information</returns>
384         public static string Version()
385         {
386             return rrd_strversion();
387         }
388
389         /// <summary>
390         /// Generates a random number for testing rrdlib
391         /// </summary>
392         /// <returns>A random integer</returns>
393         public static int Random()
394         {
395             return rrd_random();
396         }
397
398         /// <summary>
399         /// Returns the latest error from rrdlib
400         /// </summary>
401         /// <returns>A string with the error message, or an emtpy string if no error occurred</returns>
402         public static string Get_Error()
403         {
404             IntPtr ptr = rrd_get_error();
405             if (ptr == IntPtr.Zero)
406                 return "";
407             return Marshal.PtrToStringAnsi(ptr);
408         }
409
410         /// <summary>
411         /// Formats and prints information in the object to the standard output
412         /// </summary>
413         /// <param name="info">rrd_info_t object with data to print</param>
414         public static void Info_Print(rrd_info_t info)
415         {
416             IntPtr newptr = Marshal.AllocHGlobal(Marshal.SizeOf(info));
417             Marshal.StructureToPtr(info, newptr, true);
418             rrd_info_print(newptr);
419         }
420
421         /// <summary>
422         /// Converts a Char ** array of characters from the RRDLib returned as an IntPtr and converts
423         /// it to a String array given the number of items in the ptr array.
424         /// Re: http://stackoverflow.com/questions/1498931/marshalling-array-of-strings-to-char-in-c-must-be-quite-easy-if-you-know-ho
425         /// </summary>
426         /// <param name="ptr">Pointer to a character array returned from the RRDLib</param>
427         /// <param name="size">Number of items in the character array (not the number of characters)</param>
428         /// <returns>A string array</returns>
429         private static string[] GetStringArray(IntPtr ptr, UInt32 size)
430         {
431             var list = new List<string>();
432             for (int i = 0; i < size; i++)
433             {
434                 var strPtr = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr));
435                 list.Add(Marshal.PtrToStringAnsi(strPtr));
436                 ptr = new IntPtr(ptr.ToInt64() + IntPtr.Size);
437             }
438             return list.ToArray();
439         }
440     }
441 }