regressions: Enable both tests for bug 576879 (not fixed).
[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
61 ruby_guestfs_free (void *gvp)
62 {
63   guestfs_h *g = gvp;
64
65   if (g) {
66     /* As in the OCaml binding, there is a nasty, difficult to
67      * solve case here where the user deletes events in one of
68      * the callbacks that we are about to invoke, resulting in
69      * a double-free.  XXX
70      */
71     size_t len, i;
72     VALUE **roots = get_all_event_callbacks (g, &len);
73
74     /* Close the handle: this could invoke callbacks from the list
75      * above, which is why we don't want to delete them before
76      * closing the handle.
77      */
78     guestfs_close (g);
79
80     /* Now unregister the global roots. */
81     for (i = 0; i < len; ++i) {
82       rb_gc_unregister_address (roots[i]);
83       free (roots[i]);
84     }
85   }
86 }
87
88 /*
89  * call-seq:
90  *   Guestfs::Guestfs.new() -> Guestfs::Guestfs
91  *
92  * Call
93  * +guestfs_create+[http://libguestfs.org/guestfs.3.html#guestfs_create]
94  * to create a new libguestfs handle.  The handle is represented in
95  * Ruby as an instance of the Guestfs::Guestfs class.
96  */
97 static VALUE
98 ruby_guestfs_create (VALUE m)
99 {
100   guestfs_h *g;
101
102   g = guestfs_create ();
103   if (!g)
104     rb_raise (e_Error, \"failed to create guestfs handle\");
105
106   /* Don't print error messages to stderr by default. */
107   guestfs_set_error_handler (g, NULL, NULL);
108
109   /* Wrap it, and make sure the close function is called when the
110    * handle goes away.
111    */
112   return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g);
113 }
114
115 /*
116  * call-seq:
117  *   g.close() -> nil
118  *
119  * Call
120  * +guestfs_close+[http://libguestfs.org/guestfs.3.html#guestfs_close]
121  * to close the libguestfs handle.
122  */
123 static VALUE
124 ruby_guestfs_close (VALUE gv)
125 {
126   guestfs_h *g;
127   Data_Get_Struct (gv, guestfs_h, g);
128
129   ruby_guestfs_free (g);
130   DATA_PTR (gv) = NULL;
131
132   return Qnil;
133 }
134
135 /*
136  * call-seq:
137  *   g.set_event_callback(cb, event_bitmask) -> event_handle
138  *
139  * Call
140  * +guestfs_set_event_callback+[http://libguestfs.org/guestfs.3.html#guestfs_set_event_callback]
141  * to register an event callback.  This returns an event handle.
142  */
143 static VALUE
144 ruby_set_event_callback (VALUE gv, VALUE cbv, VALUE event_bitmaskv)
145 {
146   guestfs_h *g;
147   uint64_t event_bitmask;
148   int eh;
149   VALUE *root;
150   char key[64];
151
152   Data_Get_Struct (gv, guestfs_h, g);
153
154   event_bitmask = NUM2ULL (event_bitmaskv);
155
156   root = guestfs_safe_malloc (g, sizeof *root);
157   *root = cbv;
158
159   eh = guestfs_set_event_callback (g, ruby_event_callback_wrapper,
160                                    event_bitmask, 0, root);
161   if (eh == -1) {
162     free (root);
163     rb_raise (e_Error, \"%%s\", guestfs_last_error (g));
164   }
165
166   rb_gc_register_address (root);
167
168   snprintf (key, sizeof key, \"_ruby_event_%%d\", eh);
169   guestfs_set_private (g, key, root);
170
171   return INT2NUM (eh);
172 }
173
174 /*
175  * call-seq:
176  *   g.delete_event_callback(event_handle) -> nil
177  *
178  * Call
179  * +guestfs_delete_event_callback+[http://libguestfs.org/guestfs.3.html#guestfs_delete_event_callback]
180  * to delete an event callback.
181  */
182 static VALUE
183 ruby_delete_event_callback (VALUE gv, VALUE event_handlev)
184 {
185   guestfs_h *g;
186   char key[64];
187   int eh = NUM2INT (event_handlev);
188   VALUE *root;
189
190   Data_Get_Struct (gv, guestfs_h, g);
191
192   snprintf (key, sizeof key, \"_ruby_event_%%d\", eh);
193
194   root = guestfs_get_private (g, key);
195   if (root) {
196     rb_gc_unregister_address (root);
197     free (root);
198     guestfs_set_private (g, key, NULL);
199     guestfs_delete_event_callback (g, eh);
200   }
201
202   return Qnil;
203 }
204
205 static void
206 ruby_event_callback_wrapper (guestfs_h *g,
207                              void *data,
208                              uint64_t event,
209                              int event_handle,
210                              int flags,
211                              const char *buf, size_t buf_len,
212                              const uint64_t *array, size_t array_len)
213 {
214   size_t i;
215   VALUE eventv, event_handlev, bufv, arrayv;
216
217   eventv = ULL2NUM (event);
218   event_handlev = INT2NUM (event_handle);
219
220   bufv = rb_str_new (buf, buf_len);
221
222   arrayv = rb_ary_new2 (array_len);
223   for (i = 0; i < array_len; ++i)
224     rb_ary_push (arrayv, ULL2NUM (array[i]));
225
226   /* XXX If the Ruby callback raises any sort of exception then
227    * it causes the process to segfault.  I don't understand how
228    * to catch exceptions here.
229    */
230   rb_funcall (*(VALUE *) data, rb_intern (\"call\"), 4,
231               eventv, event_handlev, bufv, arrayv);
232 }
233
234 static VALUE **
235 get_all_event_callbacks (guestfs_h *g, size_t *len_rtn)
236 {
237   VALUE **r;
238   size_t i;
239   const char *key;
240   VALUE *root;
241
242   /* Count the length of the array that will be needed. */
243   *len_rtn = 0;
244   root = guestfs_first_private (g, &key);
245   while (root != NULL) {
246     if (strncmp (key, \"_ruby_event_\", strlen (\"_ruby_event_\")) == 0)
247       (*len_rtn)++;
248     root = guestfs_next_private (g, &key);
249   }
250
251   /* Copy them into the return array. */
252   r = guestfs_safe_malloc (g, sizeof (VALUE *) * (*len_rtn));
253
254   i = 0;
255   root = guestfs_first_private (g, &key);
256   while (root != NULL) {
257     if (strncmp (key, \"_ruby_event_\", strlen (\"_ruby_event_\")) == 0) {
258       r[i] = root;
259       i++;
260     }
261     root = guestfs_next_private (g, &key);
262   }
263
264   return r;
265 }
266
267 ";
268
269   List.iter (
270     fun (name, (ret, args, optargs as style), _, flags, _, shortdesc, longdesc) ->
271       (* Generate rdoc. *)
272       if not (List.mem NotInDocs flags); then (
273         let doc = replace_str longdesc "C<guestfs_" "C<g." in
274         let doc =
275           if optargs <> [] then
276             doc ^ "\n\nOptional arguments are supplied in the final hash parameter, which is a hash of the argument name to its value.  Pass an empty {} for no optional arguments."
277           else doc in
278         let doc =
279           if List.mem ProtocolLimitWarning flags then
280             doc ^ "\n\n" ^ protocol_limit_warning
281           else doc in
282         let doc =
283           if List.mem DangerWillRobinson flags then
284             doc ^ "\n\n" ^ danger_will_robinson
285           else doc in
286         let doc =
287           match deprecation_notice flags with
288           | None -> doc
289           | Some txt -> doc ^ "\n\n" ^ txt in
290         let doc = pod2text ~width:60 name doc in
291         let doc = String.concat "\n * " doc in
292         let doc = trim doc in
293
294         let args = List.map name_of_argt args in
295         let args = if optargs <> [] then args @ ["{optargs...}"] else args in
296         let args = String.concat ", " args in
297
298         let ret =
299           match ret with
300           | RErr -> "nil"
301           | RBool _ -> "[True|False]"
302           | RInt _ -> "fixnum"
303           | RInt64 _ -> "fixnum"
304           | RConstString _ -> "string"
305           | RConstOptString _ -> "string"
306           | RString _ -> "string"
307           | RBufferOut _ -> "string"
308           | RStruct _
309           | RHashtable _ -> "hash"
310           | RStringList _
311           | RStructList _ -> "list" in
312
313         pr "\
314 /*
315  * call-seq:
316  *   g.%s(%s) -> %s
317  *
318  * %s
319  *
320  * %s
321  *
322  * (For the C API documentation for this function, see
323  * +guestfs_%s+[http://libguestfs.org/guestfs.3.html#guestfs_%s]).
324  */
325 " name args ret shortdesc doc name name
326       );
327
328       (* Generate the function. *)
329       pr "static VALUE\n";
330       pr "ruby_guestfs_%s (VALUE gv" name;
331       List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) args;
332       (* XXX This makes the hash mandatory, meaning that you have
333        * to specify {} for no arguments.  We could make it so this
334        * can be omitted.  However that is a load of hassle because
335        * you have to completely change the way that arguments are
336        * passed in.  See:
337        * http://www.redhat.com/archives/libvir-list/2008-April/msg00004.html
338        *)
339       if optargs <> [] then
340         pr ", VALUE optargsv";
341       pr ")\n";
342       pr "{\n";
343       pr "  guestfs_h *g;\n";
344       pr "  Data_Get_Struct (gv, guestfs_h, g);\n";
345       pr "  if (!g)\n";
346       pr "    rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n"
347         name;
348       pr "\n";
349
350       List.iter (
351         function
352         | Pathname n | Device n | Dev_or_Path n | String n | Key n
353         | FileIn n | FileOut n ->
354             pr "  const char *%s = StringValueCStr (%sv);\n" n n;
355         | BufferIn n ->
356             pr "  Check_Type (%sv, T_STRING);\n" n;
357             pr "  const char *%s = RSTRING (%sv)->ptr;\n" n n;
358             pr "  if (!%s)\n" n;
359             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
360             pr "              \"%s\", \"%s\");\n" n name;
361             pr "  size_t %s_size = RSTRING (%sv)->len;\n" n n
362         | OptString n ->
363             pr "  const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n
364         | StringList n | DeviceList n ->
365             pr "  char **%s;\n" n;
366             pr "  Check_Type (%sv, T_ARRAY);\n" n;
367             pr "  {\n";
368             pr "    size_t i, len;\n";
369             pr "    len = RARRAY_LEN (%sv);\n" n;
370             pr "    %s = ALLOC_N (char *, len+1);\n"
371               n;
372             pr "    for (i = 0; i < len; ++i) {\n";
373             pr "      VALUE v = rb_ary_entry (%sv, i);\n" n;
374             pr "      %s[i] = StringValueCStr (v);\n" n;
375             pr "    }\n";
376             pr "    %s[len] = NULL;\n" n;
377             pr "  }\n";
378         | Bool n ->
379             pr "  int %s = RTEST (%sv);\n" n n
380         | Int n ->
381             pr "  int %s = NUM2INT (%sv);\n" n n
382         | Int64 n ->
383             pr "  long long %s = NUM2LL (%sv);\n" n n
384         | Pointer (t, n) ->
385             pr "  %s %s = (%s) (intptr_t) NUM2LL (%sv);\n" t n t n
386       ) args;
387       pr "\n";
388
389       (* Optional arguments are passed in a final hash parameter. *)
390       if optargs <> [] then (
391         let uc_name = String.uppercase name in
392         pr "  Check_Type (optargsv, T_HASH);\n";
393         pr "  struct guestfs_%s_argv optargs_s = { .bitmask = 0 };\n" name;
394         pr "  struct guestfs_%s_argv *optargs = &optargs_s;\n" name;
395         pr "  VALUE v;\n";
396         List.iter (
397           fun argt ->
398             let n = name_of_argt argt in
399             let uc_n = String.uppercase n in
400             pr "  v = rb_hash_lookup (optargsv, ID2SYM (rb_intern (\"%s\")));\n" n;
401             pr "  if (v != Qnil) {\n";
402             (match argt with
403              | Bool n ->
404                  pr "    optargs_s.%s = RTEST (v);\n" n;
405              | Int n ->
406                  pr "    optargs_s.%s = NUM2INT (v);\n" n;
407              | Int64 n ->
408                  pr "    optargs_s.%s = NUM2LL (v);\n" n;
409              | String _ ->
410                  pr "    optargs_s.%s = StringValueCStr (v);\n" n
411              | _ -> assert false
412             );
413             pr "    optargs_s.bitmask |= GUESTFS_%s_%s_BITMASK;\n" uc_name uc_n;
414             pr "  }\n";
415         ) optargs;
416         pr "\n";
417       );
418
419       (match ret with
420        | RErr | RInt _ | RBool _ -> pr "  int r;\n"
421        | RInt64 _ -> pr "  int64_t r;\n"
422        | RConstString _ | RConstOptString _ ->
423            pr "  const char *r;\n"
424        | RString _ -> pr "  char *r;\n"
425        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
426        | RStruct (_, typ) -> pr "  struct guestfs_%s *r;\n" typ
427        | RStructList (_, typ) ->
428            pr "  struct guestfs_%s_list *r;\n" typ
429        | RBufferOut _ ->
430            pr "  char *r;\n";
431            pr "  size_t size;\n"
432       );
433       pr "\n";
434
435       if optargs = [] then
436         pr "  r = guestfs_%s " name
437       else
438         pr "  r = guestfs_%s_argv " name;
439       generate_c_call_args ~handle:"g" style;
440       pr ";\n";
441
442       List.iter (
443         function
444         | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
445         | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
446         | BufferIn _ | Pointer _ -> ()
447         | StringList n | DeviceList n ->
448             pr "  free (%s);\n" n
449       ) args;
450
451       (match errcode_of_ret ret with
452        | `CannotReturnError -> ()
453        | `ErrorIsMinusOne ->
454            pr "  if (r == -1)\n";
455            pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n"
456        | `ErrorIsNULL ->
457            pr "  if (r == NULL)\n";
458            pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n"
459       );
460       pr "\n";
461
462       (match ret with
463        | RErr ->
464            pr "  return Qnil;\n"
465        | RInt _ | RBool _ ->
466            pr "  return INT2NUM (r);\n"
467        | RInt64 _ ->
468            pr "  return ULL2NUM (r);\n"
469        | RConstString _ ->
470            pr "  return rb_str_new2 (r);\n";
471        | RConstOptString _ ->
472            pr "  if (r)\n";
473            pr "    return rb_str_new2 (r);\n";
474            pr "  else\n";
475            pr "    return Qnil;\n";
476        | RString _ ->
477            pr "  VALUE rv = rb_str_new2 (r);\n";
478            pr "  free (r);\n";
479            pr "  return rv;\n";
480        | RStringList _ ->
481            pr "  size_t i, len = 0;\n";
482            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
483            pr "  VALUE rv = rb_ary_new2 (len);\n";
484            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
485            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
486            pr "    free (r[i]);\n";
487            pr "  }\n";
488            pr "  free (r);\n";
489            pr "  return rv;\n"
490        | RStruct (_, typ) ->
491            let cols = cols_of_struct typ in
492            generate_ruby_struct_code typ cols
493        | RStructList (_, typ) ->
494            let cols = cols_of_struct typ in
495            generate_ruby_struct_list_code typ cols
496        | RHashtable _ ->
497            pr "  VALUE rv = rb_hash_new ();\n";
498            pr "  size_t i;\n";
499            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
500            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
501            pr "    free (r[i]);\n";
502            pr "    free (r[i+1]);\n";
503            pr "  }\n";
504            pr "  free (r);\n";
505            pr "  return rv;\n"
506        | RBufferOut _ ->
507            pr "  VALUE rv = rb_str_new (r, size);\n";
508            pr "  free (r);\n";
509            pr "  return rv;\n";
510       );
511
512       pr "}\n";
513       pr "\n"
514   ) all_functions;
515
516   pr "\
517 /* Initialize the module. */
518 void Init__guestfs ()
519 {
520   m_guestfs = rb_define_module (\"Guestfs\");
521   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
522   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
523
524 #ifdef HAVE_RB_DEFINE_ALLOC_FUNC
525   rb_define_alloc_func (c_guestfs, ruby_guestfs_create);
526 #endif
527
528   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
529   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
530   rb_define_method (c_guestfs, \"set_event_callback\",
531                     ruby_set_event_callback, 2);
532   rb_define_method (c_guestfs, \"delete_event_callback\",
533                     ruby_delete_event_callback, 1);
534
535 ";
536
537   (* Constants. *)
538   List.iter (
539     fun (name, bitmask) ->
540       pr "  rb_define_const (m_guestfs, \"EVENT_%s\",\n"
541         (String.uppercase name);
542       pr "                   ULL2NUM (UINT64_C (0x%x)));\n" bitmask;
543   ) events;
544   pr "\n";
545
546   (* Methods. *)
547   List.iter (
548     fun (name, (_, args, optargs), _, _, _, _, _) ->
549       let nr_args = List.length args + if optargs <> [] then 1 else 0 in
550       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
551       pr "        ruby_guestfs_%s, %d);\n" name nr_args
552   ) all_functions;
553
554   pr "}\n"
555
556 (* Ruby code to return a struct. *)
557 and generate_ruby_struct_code typ cols =
558   pr "  VALUE rv = rb_hash_new ();\n";
559   List.iter (
560     function
561     | name, FString ->
562         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name
563     | name, FBuffer ->
564         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name
565     | name, FUUID ->
566         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, 32));\n" name name
567     | name, (FBytes|FUInt64) ->
568         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
569     | name, FInt64 ->
570         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), LL2NUM (r->%s));\n" name name
571     | name, FUInt32 ->
572         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), UINT2NUM (r->%s));\n" name name
573     | name, FInt32 ->
574         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), INT2NUM (r->%s));\n" name name
575     | name, FOptPercent ->
576         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->%s));\n" name name
577     | name, FChar -> (* XXX wrong? *)
578         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
579   ) cols;
580   pr "  guestfs_free_%s (r);\n" typ;
581   pr "  return rv;\n"
582
583 (* Ruby code to return a struct list. *)
584 and generate_ruby_struct_list_code typ cols =
585   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
586   pr "  size_t i;\n";
587   pr "  for (i = 0; i < r->len; ++i) {\n";
588   pr "    VALUE hv = rb_hash_new ();\n";
589   List.iter (
590     function
591     | name, FString ->
592         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
593     | name, FBuffer ->
594         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
595     | name, FUUID ->
596         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
597     | name, (FBytes|FUInt64) ->
598         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
599     | name, FInt64 ->
600         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), LL2NUM (r->val[i].%s));\n" name name
601     | name, FUInt32 ->
602         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), UINT2NUM (r->val[i].%s));\n" name name
603     | name, FInt32 ->
604         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), INT2NUM (r->val[i].%s));\n" name name
605     | name, FOptPercent ->
606         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
607     | name, FChar -> (* XXX wrong? *)
608         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
609   ) cols;
610   pr "    rb_ary_push (rv, hv);\n";
611   pr "  }\n";
612   pr "  guestfs_free_%s_list (r);\n" typ;
613   pr "  return rv;\n"