b237c7f0da77d59fd2341f645525a7b959bcddd3
[libguestfs.git] / generator / generator_ruby.ml
1 (* libguestfs
2  * Copyright (C) 2009-2011 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *)
18
19 (* Please read generator/README first. *)
20
21 open Printf
22
23 open Generator_types
24 open Generator_utils
25 open Generator_pr
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
30 open Generator_c
31 open Generator_events
32
33 (* Generate ruby bindings. *)
34 let rec generate_ruby_c () =
35   generate_header CStyle LGPLv2plus;
36
37   pr "\
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdint.h>
41
42 #include <ruby.h>
43
44 #include \"guestfs.h\"
45
46 #include \"extconf.h\"
47
48 /* For Ruby < 1.9 */
49 #ifndef RARRAY_LEN
50 #define RARRAY_LEN(r) (RARRAY((r))->len)
51 #endif
52
53 static VALUE m_guestfs;                 /* guestfs module */
54 static VALUE c_guestfs;                 /* guestfs_h handle */
55 static VALUE e_Error;                   /* used for all errors */
56
57 static void ruby_event_callback_wrapper (guestfs_h *g, void *data, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len);
58 static VALUE **get_all_event_callbacks (guestfs_h *g, size_t *len_rtn);
59
60 static void ruby_guestfs_free (void *gvp)
61 {
62   guestfs_h *g = gvp;
63
64   if (g) {
65     /* As in the OCaml binding, there is a nasty, difficult to
66      * solve case here where the user deletes events in one of
67      * the callbacks that we are about to invoke, resulting in
68      * a double-free.  XXX
69      */
70     size_t len, i;
71     VALUE **roots = get_all_event_callbacks (g, &len);
72
73     /* Close the handle: this could invoke callbacks from the list
74      * above, which is why we don't want to delete them before
75      * closing the handle.
76      */
77     guestfs_close (g);
78
79     /* Now unregister the global roots. */
80     for (i = 0; i < len; ++i) {
81       rb_gc_unregister_address (roots[i]);
82       free (roots[i]);
83     }
84   }
85 }
86
87 static VALUE ruby_guestfs_create (VALUE m)
88 {
89   guestfs_h *g;
90
91   g = guestfs_create ();
92   if (!g)
93     rb_raise (e_Error, \"failed to create guestfs handle\");
94
95   /* Don't print error messages to stderr by default. */
96   guestfs_set_error_handler (g, NULL, NULL);
97
98   /* Wrap it, and make sure the close function is called when the
99    * handle goes away.
100    */
101   return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g);
102 }
103
104 static VALUE ruby_guestfs_close (VALUE gv)
105 {
106   guestfs_h *g;
107   Data_Get_Struct (gv, guestfs_h, g);
108
109   ruby_guestfs_free (g);
110   DATA_PTR (gv) = NULL;
111
112   return Qnil;
113 }
114
115 static VALUE
116 ruby_set_event_callback (VALUE gv, VALUE cbv, VALUE event_bitmaskv)
117 {
118   guestfs_h *g;
119   uint64_t event_bitmask;
120   int eh;
121   VALUE *root;
122   char key[64];
123
124   Data_Get_Struct (gv, guestfs_h, g);
125
126   event_bitmask = NUM2ULL (event_bitmaskv);
127
128   root = guestfs_safe_malloc (g, sizeof *root);
129   *root = cbv;
130
131   eh = guestfs_set_event_callback (g, ruby_event_callback_wrapper,
132                                    event_bitmask, 0, root);
133   if (eh == -1) {
134     free (root);
135     rb_raise (e_Error, \"%%s\", guestfs_last_error (g));
136   }
137
138   rb_gc_register_address (root);
139
140   snprintf (key, sizeof key, \"_ruby_event_%%d\", eh);
141   guestfs_set_private (g, key, root);
142
143   return INT2NUM (eh);
144 }
145
146 static VALUE
147 ruby_delete_event_callback (VALUE gv, VALUE event_handlev)
148 {
149   guestfs_h *g;
150   char key[64];
151   int eh = NUM2INT (event_handlev);
152   VALUE *root;
153
154   Data_Get_Struct (gv, guestfs_h, g);
155
156   snprintf (key, sizeof key, \"_ruby_event_%%d\", eh);
157
158   root = guestfs_get_private (g, key);
159   if (root) {
160     rb_gc_unregister_address (root);
161     free (root);
162     guestfs_set_private (g, key, NULL);
163     guestfs_delete_event_callback (g, eh);
164   }
165
166   return Qnil;
167 }
168
169 static void
170 ruby_event_callback_wrapper (guestfs_h *g,
171                              void *data,
172                              uint64_t event,
173                              int event_handle,
174                              int flags,
175                              const char *buf, size_t buf_len,
176                              const uint64_t *array, size_t array_len)
177 {
178   size_t i;
179   VALUE eventv, event_handlev, bufv, arrayv;
180
181   eventv = ULL2NUM (event);
182   event_handlev = INT2NUM (event_handle);
183
184   bufv = rb_str_new (buf, buf_len);
185
186   arrayv = rb_ary_new2 (array_len);
187   for (i = 0; i < array_len; ++i)
188     rb_ary_push (arrayv, ULL2NUM (array[i]));
189
190   /* XXX If the Ruby callback raises any sort of exception then
191    * it causes the process to segfault.  I don't understand how
192    * to catch exceptions here.
193    */
194   rb_funcall (*(VALUE *) data, rb_intern (\"call\"), 4,
195               eventv, event_handlev, bufv, arrayv);
196 }
197
198 static VALUE **
199 get_all_event_callbacks (guestfs_h *g, size_t *len_rtn)
200 {
201   VALUE **r;
202   size_t i;
203   const char *key;
204   VALUE *root;
205
206   /* Count the length of the array that will be needed. */
207   *len_rtn = 0;
208   root = guestfs_first_private (g, &key);
209   while (root != NULL) {
210     if (strncmp (key, \"_ruby_event_\", strlen (\"_ruby_event_\")) == 0)
211       (*len_rtn)++;
212     root = guestfs_next_private (g, &key);
213   }
214
215   /* Copy them into the return array. */
216   r = guestfs_safe_malloc (g, sizeof (VALUE *) * (*len_rtn));
217
218   i = 0;
219   root = guestfs_first_private (g, &key);
220   while (root != NULL) {
221     if (strncmp (key, \"_ruby_event_\", strlen (\"_ruby_event_\")) == 0) {
222       r[i] = root;
223       i++;
224     }
225     root = guestfs_next_private (g, &key);
226   }
227
228   return r;
229 }
230
231 ";
232
233   List.iter (
234     fun (name, (ret, args, optargs as style), _, _, _, _, _) ->
235       pr "static VALUE ruby_guestfs_%s (VALUE gv" name;
236       List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) args;
237       (* XXX This makes the hash mandatory, meaning that you have
238        * to specify {} for no arguments.  We could make it so this
239        * can be omitted.  However that is a load of hassle because
240        * you have to completely change the way that arguments are
241        * passed in.  See:
242        * http://www.redhat.com/archives/libvir-list/2008-April/msg00004.html
243        *)
244       if optargs <> [] then
245         pr ", VALUE optargsv";
246       pr ")\n";
247       pr "{\n";
248       pr "  guestfs_h *g;\n";
249       pr "  Data_Get_Struct (gv, guestfs_h, g);\n";
250       pr "  if (!g)\n";
251       pr "    rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n"
252         name;
253       pr "\n";
254
255       List.iter (
256         function
257         | Pathname n | Device n | Dev_or_Path n | String n | Key n
258         | FileIn n | FileOut n ->
259             pr "  Check_Type (%sv, T_STRING);\n" n;
260             pr "  const char *%s = StringValueCStr (%sv);\n" n n;
261             pr "  if (!%s)\n" n;
262             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
263             pr "              \"%s\", \"%s\");\n" n name
264         | BufferIn n ->
265             pr "  Check_Type (%sv, T_STRING);\n" n;
266             pr "  const char *%s = RSTRING (%sv)->ptr;\n" n n;
267             pr "  if (!%s)\n" n;
268             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
269             pr "              \"%s\", \"%s\");\n" n name;
270             pr "  size_t %s_size = RSTRING (%sv)->len;\n" n n
271         | OptString n ->
272             pr "  const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n
273         | StringList n | DeviceList n ->
274             pr "  char **%s;\n" n;
275             pr "  Check_Type (%sv, T_ARRAY);\n" n;
276             pr "  {\n";
277             pr "    size_t i, len;\n";
278             pr "    len = RARRAY_LEN (%sv);\n" n;
279             pr "    %s = guestfs_safe_malloc (g, sizeof (char *) * (len+1));\n"
280               n;
281             pr "    for (i = 0; i < len; ++i) {\n";
282             pr "      VALUE v = rb_ary_entry (%sv, i);\n" n;
283             pr "      %s[i] = StringValueCStr (v);\n" n;
284             pr "    }\n";
285             pr "    %s[len] = NULL;\n" n;
286             pr "  }\n";
287         | Bool n ->
288             pr "  int %s = RTEST (%sv);\n" n n
289         | Int n ->
290             pr "  int %s = NUM2INT (%sv);\n" n n
291         | Int64 n ->
292             pr "  long long %s = NUM2LL (%sv);\n" n n
293         | Pointer (t, n) ->
294             pr "  %s %s = (%s) (intptr_t) NUM2LL (%sv);\n" t n t n
295       ) args;
296       pr "\n";
297
298       (* Optional arguments are passed in a final hash parameter. *)
299       if optargs <> [] then (
300         let uc_name = String.uppercase name in
301         pr "  Check_Type (optargsv, T_HASH);\n";
302         pr "  struct guestfs_%s_argv optargs_s = { .bitmask = 0 };\n" name;
303         pr "  struct guestfs_%s_argv *optargs = &optargs_s;\n" name;
304         pr "  VALUE v;\n";
305         List.iter (
306           fun argt ->
307             let n = name_of_argt argt in
308             let uc_n = String.uppercase n in
309             pr "  v = rb_hash_lookup (optargsv, ID2SYM (rb_intern (\"%s\")));\n" n;
310             pr "  if (v != Qnil) {\n";
311             (match argt with
312              | Bool n ->
313                  pr "    optargs_s.%s = RTEST (v);\n" n;
314              | Int n ->
315                  pr "    optargs_s.%s = NUM2INT (v);\n" n;
316              | Int64 n ->
317                  pr "    optargs_s.%s = NUM2LL (v);\n" n;
318              | String _ ->
319                  pr "    Check_Type (v, T_STRING);\n";
320                  pr "    optargs_s.%s = StringValueCStr (v);\n" n
321              | _ -> assert false
322             );
323             pr "    optargs_s.bitmask |= GUESTFS_%s_%s_BITMASK;\n" uc_name uc_n;
324             pr "  }\n";
325         ) optargs;
326         pr "\n";
327       );
328
329       (match ret with
330        | RErr | RInt _ | RBool _ -> pr "  int r;\n"
331        | RInt64 _ -> pr "  int64_t r;\n"
332        | RConstString _ | RConstOptString _ ->
333            pr "  const char *r;\n"
334        | RString _ -> pr "  char *r;\n"
335        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
336        | RStruct (_, typ) -> pr "  struct guestfs_%s *r;\n" typ
337        | RStructList (_, typ) ->
338            pr "  struct guestfs_%s_list *r;\n" typ
339        | RBufferOut _ ->
340            pr "  char *r;\n";
341            pr "  size_t size;\n"
342       );
343       pr "\n";
344
345       if optargs = [] then
346         pr "  r = guestfs_%s " name
347       else
348         pr "  r = guestfs_%s_argv " name;
349       generate_c_call_args ~handle:"g" style;
350       pr ";\n";
351
352       List.iter (
353         function
354         | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
355         | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
356         | BufferIn _ | Pointer _ -> ()
357         | StringList n | DeviceList n ->
358             pr "  free (%s);\n" n
359       ) args;
360
361       (match errcode_of_ret ret with
362        | `CannotReturnError -> ()
363        | `ErrorIsMinusOne ->
364            pr "  if (r == -1)\n";
365            pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n"
366        | `ErrorIsNULL ->
367            pr "  if (r == NULL)\n";
368            pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n"
369       );
370       pr "\n";
371
372       (match ret with
373        | RErr ->
374            pr "  return Qnil;\n"
375        | RInt _ | RBool _ ->
376            pr "  return INT2NUM (r);\n"
377        | RInt64 _ ->
378            pr "  return ULL2NUM (r);\n"
379        | RConstString _ ->
380            pr "  return rb_str_new2 (r);\n";
381        | RConstOptString _ ->
382            pr "  if (r)\n";
383            pr "    return rb_str_new2 (r);\n";
384            pr "  else\n";
385            pr "    return Qnil;\n";
386        | RString _ ->
387            pr "  VALUE rv = rb_str_new2 (r);\n";
388            pr "  free (r);\n";
389            pr "  return rv;\n";
390        | RStringList _ ->
391            pr "  size_t i, len = 0;\n";
392            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
393            pr "  VALUE rv = rb_ary_new2 (len);\n";
394            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
395            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
396            pr "    free (r[i]);\n";
397            pr "  }\n";
398            pr "  free (r);\n";
399            pr "  return rv;\n"
400        | RStruct (_, typ) ->
401            let cols = cols_of_struct typ in
402            generate_ruby_struct_code typ cols
403        | RStructList (_, typ) ->
404            let cols = cols_of_struct typ in
405            generate_ruby_struct_list_code typ cols
406        | RHashtable _ ->
407            pr "  VALUE rv = rb_hash_new ();\n";
408            pr "  size_t i;\n";
409            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
410            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
411            pr "    free (r[i]);\n";
412            pr "    free (r[i+1]);\n";
413            pr "  }\n";
414            pr "  free (r);\n";
415            pr "  return rv;\n"
416        | RBufferOut _ ->
417            pr "  VALUE rv = rb_str_new (r, size);\n";
418            pr "  free (r);\n";
419            pr "  return rv;\n";
420       );
421
422       pr "}\n";
423       pr "\n"
424   ) all_functions;
425
426   pr "\
427 /* Initialize the module. */
428 void Init__guestfs ()
429 {
430   m_guestfs = rb_define_module (\"Guestfs\");
431   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
432   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
433
434 #ifdef HAVE_RB_DEFINE_ALLOC_FUNC
435   rb_define_alloc_func (c_guestfs, ruby_guestfs_create);
436 #endif
437
438   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
439   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
440   rb_define_method (c_guestfs, \"set_event_callback\",
441                     ruby_set_event_callback, 2);
442   rb_define_method (c_guestfs, \"delete_event_callback\",
443                     ruby_delete_event_callback, 1);
444
445 ";
446
447   (* Constants. *)
448   List.iter (
449     fun (name, bitmask) ->
450       pr "  rb_define_const (m_guestfs, \"EVENT_%s\",\n"
451         (String.uppercase name);
452       pr "                   ULL2NUM (UINT64_C (0x%x)));\n" bitmask;
453   ) events;
454   pr "\n";
455
456   (* Methods. *)
457   List.iter (
458     fun (name, (_, args, optargs), _, _, _, _, _) ->
459       let nr_args = List.length args + if optargs <> [] then 1 else 0 in
460       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
461       pr "        ruby_guestfs_%s, %d);\n" name nr_args
462   ) all_functions;
463
464   pr "}\n"
465
466 (* Ruby code to return a struct. *)
467 and generate_ruby_struct_code typ cols =
468   pr "  VALUE rv = rb_hash_new ();\n";
469   List.iter (
470     function
471     | name, FString ->
472         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name
473     | name, FBuffer ->
474         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name
475     | name, FUUID ->
476         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, 32));\n" name name
477     | name, (FBytes|FUInt64) ->
478         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
479     | name, FInt64 ->
480         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), LL2NUM (r->%s));\n" name name
481     | name, FUInt32 ->
482         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), UINT2NUM (r->%s));\n" name name
483     | name, FInt32 ->
484         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), INT2NUM (r->%s));\n" name name
485     | name, FOptPercent ->
486         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->%s));\n" name name
487     | name, FChar -> (* XXX wrong? *)
488         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
489   ) cols;
490   pr "  guestfs_free_%s (r);\n" typ;
491   pr "  return rv;\n"
492
493 (* Ruby code to return a struct list. *)
494 and generate_ruby_struct_list_code typ cols =
495   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
496   pr "  size_t i;\n";
497   pr "  for (i = 0; i < r->len; ++i) {\n";
498   pr "    VALUE hv = rb_hash_new ();\n";
499   List.iter (
500     function
501     | name, FString ->
502         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
503     | name, FBuffer ->
504         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, r->val[i].%s_len));\n" name name name
505     | name, FUUID ->
506         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
507     | name, (FBytes|FUInt64) ->
508         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
509     | name, FInt64 ->
510         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), LL2NUM (r->val[i].%s));\n" name name
511     | name, FUInt32 ->
512         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), UINT2NUM (r->val[i].%s));\n" name name
513     | name, FInt32 ->
514         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), INT2NUM (r->val[i].%s));\n" name name
515     | name, FOptPercent ->
516         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
517     | name, FChar -> (* XXX wrong? *)
518         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
519   ) cols;
520   pr "    rb_ary_push (rv, hv);\n";
521   pr "  }\n";
522   pr "  guestfs_free_%s_list (r);\n" typ;
523   pr "  return rv;\n"