X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fgenerator.ml;h=c0a47404b687217117f833a8083b7093075e4f6f;hb=92804dec7c4982d2039f81586bc4a5cacb46217b;hp=4653d65cfdc219bf3434bc1fbad4d20ad99ce9fd;hpb=05712b2457a44ee0f0020eced77db03c2aa419a1;p=libguestfs.git diff --git a/src/generator.ml b/src/generator.ml index 4653d65..c0a4740 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -1322,6 +1322,31 @@ let replace_char s c1 c2 = done; if not !r then s else s2 +let isspace c = + c = ' ' + (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *) + +let triml ?(test = isspace) str = + let i = ref 0 in + let n = ref (String.length str) in + while !n > 0 && test str.[!i]; do + decr n; + incr i + done; + if !i = 0 then str + else String.sub str !i !n + +let trimr ?(test = isspace) str = + let n = ref (String.length str) in + while !n > 0 && test str.[!n-1]; do + decr n + done; + if !n = String.length str then str + else String.sub str 0 !n + +let trim ?(test = isspace) str = + trimr ~test (triml ~test str) + let rec find s sub = let len = String.length s in let sublen = String.length sub in @@ -2369,6 +2394,7 @@ and generate_daemon_actions () = pr " reply_with_error (\"%%s\", err);\n"; pr " free (out);\n"; pr " free (err);\n"; + pr " free (ret);\n"; pr " return NULL;\n"; pr " }\n"; pr "\n"; @@ -4701,27 +4727,378 @@ initlibguestfsmod (void) and generate_python_py () = generate_header HashStyle LGPLv2; - pr "import libguestfsmod\n"; - pr "\n"; - pr "class GuestFS:\n"; - pr " def __init__ (self):\n"; - pr " self._o = libguestfsmod.create ()\n"; - pr "\n"; - pr " def __del__ (self):\n"; - pr " libguestfsmod.close (self._o)\n"; - pr "\n"; + pr "\ +u\"\"\"Python bindings for libguestfs + +import guestfs +g = guestfs.GuestFS () +g.add_drive (\"guest.img\") +g.launch () +g.wait_ready () +parts = g.list_partitions () + +The guestfs module provides a Python binding to the libguestfs API +for examining and modifying virtual machine disk images. + +Amongst the things this is good for: making batch configuration +changes to guests, getting disk used/free statistics (see also: +virt-df), migrating between virtualization systems (see also: +virt-p2v), performing partial backups, performing partial guest +clones, cloning guests and changing registry/UUID/hostname info, and +much else besides. + +Libguestfs uses Linux kernel and qemu code, and can access any type of +guest filesystem that Linux and qemu can, including but not limited +to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition +schemes, qcow, qcow2, vmdk. + +Libguestfs provides ways to enumerate guest storage (eg. partitions, +LVs, what filesystem is in each LV, etc.). It can also run commands +in the context of the guest. Also you can access filesystems over FTP. + +Errors which happen while using the API are turned into Python +RuntimeError exceptions. + +To create a guestfs handle you usually have to perform the following +sequence of calls: + +# Create the handle, call add_drive at least once, and possibly +# several times if the guest has multiple block devices: +g = guestfs.GuestFS () +g.add_drive (\"guest.img\") + +# Launch the qemu subprocess and wait for it to become ready: +g.launch () +g.wait_ready () + +# Now you can issue commands, for example: +logvols = g.lvs () + +\"\"\" + +import libguestfsmod + +class GuestFS: + \"\"\"Instances of this class are libguestfs API handles.\"\"\" + + def __init__ (self): + \"\"\"Create a new libguestfs handle.\"\"\" + self._o = libguestfsmod.create () + + def __del__ (self): + libguestfsmod.close (self._o) + +"; List.iter ( - fun (name, style, _, _, _, _, _) -> + fun (name, style, _, flags, _, _, longdesc) -> + let doc = replace_str longdesc "C doc + | RStringList _ -> + doc ^ "\n\nThis function returns a list of strings." + | RIntBool _ -> + doc ^ "\n\nThis function returns a tuple (int, bool).\n" + | RPVList _ -> + doc ^ "\n\nThis function returns a list of PVs. Each PV is represented as a dictionary." + | RVGList _ -> + doc ^ "\n\nThis function returns a list of VGs. Each VG is represented as a dictionary." + | RLVList _ -> + doc ^ "\n\nThis function returns a list of LVs. Each LV is represented as a dictionary." + | RStat _ -> + doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the stat structure." + | RStatVFS _ -> + doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure." + | RHashtable _ -> + doc ^ "\n\nThis function returns a dictionary." in + let doc = + if List.mem ProtocolLimitWarning flags then + doc ^ "\n\n" ^ protocol_limit_warning + else doc in + let doc = + if List.mem DangerWillRobinson flags then + doc ^ "\n\n" ^ danger_will_robinson + else doc in + let doc = pod2text ~width:60 name doc in + let doc = List.map (fun line -> replace_str line "\\" "\\\\") doc in + let doc = String.concat "\n " doc in + pr " def %s " name; generate_call_args ~handle:"self" style; pr ":\n"; + pr " u\"\"\"%s\"\"\"\n" doc; pr " return libguestfsmod.%s " name; generate_call_args ~handle:"self._o" style; pr "\n"; pr "\n"; ) all_functions +(* Useful if you need the longdesc POD text as plain text. Returns a + * list of lines. + *) +and pod2text ~width name longdesc = + let filename, chan = Filename.open_temp_file "gen" ".tmp" in + fprintf chan "=head1 %s\n\n%s\n" name longdesc; + close_out chan; + let cmd = sprintf "pod2text -w %d %s" width (Filename.quote filename) in + let chan = Unix.open_process_in cmd in + let lines = ref [] in + let rec loop i = + let line = input_line chan in + if i = 1 then (* discard the first line of output *) + loop (i+1) + else ( + let line = triml line in + lines := line :: !lines; + loop (i+1) + ) in + let lines = try loop 1 with End_of_file -> List.rev !lines in + Unix.unlink filename; + match Unix.close_process_in chan with + | Unix.WEXITED 0 -> lines + | Unix.WEXITED i -> + failwithf "pod2text: process exited with non-zero status (%d)" i + | Unix.WSIGNALED i | Unix.WSTOPPED i -> + failwithf "pod2text: process signalled or stopped by signal %d" i + +(* Generate ruby bindings. *) +and generate_ruby_c () = + generate_header CStyle LGPLv2; + + pr "\ +#include +#include + +#include + +#include \"guestfs.h\" + +#include \"extconf.h\" + +static VALUE m_guestfs; /* guestfs module */ +static VALUE c_guestfs; /* guestfs_h handle */ +static VALUE e_Error; /* used for all errors */ + +static void ruby_guestfs_free (void *p) +{ + if (!p) return; + guestfs_close ((guestfs_h *) p); +} + +static VALUE ruby_guestfs_create (VALUE m) +{ + guestfs_h *g; + + g = guestfs_create (); + if (!g) + rb_raise (e_Error, \"failed to create guestfs handle\"); + + /* Don't print error messages to stderr by default. */ + guestfs_set_error_handler (g, NULL, NULL); + + /* Wrap it, and make sure the close function is called when the + * handle goes away. + */ + return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g); +} + +static VALUE ruby_guestfs_close (VALUE gv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + + ruby_guestfs_free (g); + DATA_PTR (gv) = NULL; + + return Qnil; +} + +"; + + List.iter ( + fun (name, style, _, _, _, _, _) -> + pr "static VALUE ruby_guestfs_%s (VALUE gv" name; + List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) (snd style); + pr ")\n"; + pr "{\n"; + pr " guestfs_h *g;\n"; + pr " Data_Get_Struct (gv, guestfs_h, g);\n"; + pr " if (!g)\n"; + pr " rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n" + name; + pr "\n"; + + List.iter ( + function + | String n -> + pr " const char *%s = StringValueCStr (%sv);\n" n n; + pr " if (!%s)\n" n; + pr " rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n"; + pr " \"%s\", \"%s\");\n" n name + | OptString n -> + pr " const char *%s = StringValueCStr (%sv);\n" n n + | StringList n -> + pr " char **%s;" n; + pr " {\n"; + pr " int i, len;\n"; + pr " len = RARRAY_LEN (%sv);\n" n; + pr " %s = malloc (sizeof (char *) * (len+1));\n" n; + pr " for (i = 0; i < len; ++i) {\n"; + pr " VALUE v = rb_ary_entry (%sv, i);\n" n; + pr " %s[i] = StringValueCStr (v);\n" n; + pr " }\n"; + pr " }\n"; + | Bool n + | Int n -> + pr " int %s = NUM2INT (%sv);\n" n n + ) (snd style); + pr "\n"; + + let error_code = + match fst style with + | RErr | RInt _ | RBool _ -> pr " int r;\n"; "-1" + | RInt64 _ -> pr " int64_t r;\n"; "-1" + | RConstString _ -> pr " const char *r;\n"; "NULL" + | RString _ -> pr " char *r;\n"; "NULL" + | RStringList _ | RHashtable _ -> pr " char **r;\n"; "NULL" + | RIntBool _ -> pr " struct guestfs_int_bool *r;\n"; "NULL" + | RPVList n -> pr " struct guestfs_lvm_pv_list *r;\n"; "NULL" + | RVGList n -> pr " struct guestfs_lvm_vg_list *r;\n"; "NULL" + | RLVList n -> pr " struct guestfs_lvm_lv_list *r;\n"; "NULL" + | RStat n -> pr " struct guestfs_stat *r;\n"; "NULL" + | RStatVFS n -> pr " struct guestfs_statvfs *r;\n"; "NULL" in + pr "\n"; + + pr " r = guestfs_%s " name; + generate_call_args ~handle:"g" style; + pr ";\n"; + + List.iter ( + function + | String _ | OptString _ | Bool _ | Int _ -> () + | StringList n -> + pr " free (%s);\n" n + ) (snd style); + + pr " if (r == %s)\n" error_code; + pr " rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n"; + pr "\n"; + + (match fst style with + | RErr -> + pr " return Qnil;\n" + | RInt _ | RBool _ -> + pr " return INT2NUM (r);\n" + | RInt64 _ -> + pr " return ULL2NUM (r);\n" + | RConstString _ -> + pr " return rb_str_new2 (r);\n"; + | RString _ -> + pr " VALUE rv = rb_str_new2 (r);\n"; + pr " free (r);\n"; + pr " return rv;\n"; + | RStringList _ -> + pr " int i, len = 0;\n"; + pr " for (i = 0; r[i] != NULL; ++i) len++;\n"; + pr " VALUE rv = rb_ary_new2 (len);\n"; + pr " for (i = 0; r[i] != NULL; ++i) {\n"; + pr " rb_ary_push (rv, rb_str_new2 (r[i]));\n"; + pr " free (r[i]);\n"; + pr " }\n"; + pr " free (r);\n"; + pr " return rv;\n" + | RIntBool _ -> + pr " VALUE rv = rb_ary_new2 (2);\n"; + pr " rb_ary_push (rv, INT2NUM (r->i));\n"; + pr " rb_ary_push (rv, INT2NUM (r->b));\n"; + pr " guestfs_free_int_bool (r);\n"; + pr " return rv;\n" + | RPVList n -> + generate_ruby_lvm_code "pv" pv_cols + | RVGList n -> + generate_ruby_lvm_code "vg" vg_cols + | RLVList n -> + generate_ruby_lvm_code "lv" lv_cols + | RStat n -> + pr " VALUE rv = rb_hash_new ();\n"; + List.iter ( + function + | name, `Int -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name + ) stat_cols; + pr " free (r);\n"; + pr " return rv;\n" + | RStatVFS n -> + pr " VALUE rv = rb_hash_new ();\n"; + List.iter ( + function + | name, `Int -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name + ) statvfs_cols; + pr " free (r);\n"; + pr " return rv;\n" + | RHashtable _ -> + pr " VALUE rv = rb_hash_new ();\n"; + pr " int i;\n"; + pr " for (i = 0; r[i] != NULL; i+=2) {\n"; + pr " rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n"; + pr " free (r[i]);\n"; + pr " free (r[i+1]);\n"; + pr " }\n"; + pr " free (r);\n"; + pr " return rv;\n" + ); + + pr "}\n"; + pr "\n" + ) all_functions; + + pr "\ +/* Initialize the module. */ +void Init__guestfs () +{ + m_guestfs = rb_define_module (\"Guestfs\"); + c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject); + e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError); + + rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0); + rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0); + +"; + (* Define the rest of the methods. *) + List.iter ( + fun (name, style, _, _, _, _, _) -> + pr " rb_define_method (c_guestfs, \"%s\",\n" name; + pr " ruby_guestfs_%s, %d);\n" name (List.length (snd style)) + ) all_functions; + + pr "}\n" + +(* Ruby code to return an LVM struct list. *) +and generate_ruby_lvm_code typ cols = + pr " VALUE rv = rb_ary_new2 (r->len);\n"; + pr " int i;\n"; + pr " for (i = 0; i < r->len; ++i) {\n"; + pr " VALUE hv = rb_hash_new ();\n"; + List.iter ( + function + | name, `String -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name + | name, `UUID -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name + | name, `Bytes + | name, `Int -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name + | name, `OptPercent -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name + ) cols; + pr " rb_ary_push (rv, hv);\n"; + pr " }\n"; + pr " guestfs_free_lvm_%s_list (r);\n" typ; + pr " return rv;\n" + let output_to filename = let filename_new = filename ^ ".new" in chan := open_out filename_new; @@ -4821,3 +5198,7 @@ Run it from the top source directory using the command let close = output_to "python/guestfs.py" in generate_python_py (); close (); + + let close = output_to "ruby/ext/guestfs/_guestfs.c" in + generate_ruby_c (); + close ();