X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fgenerator.ml;h=20f7ac012915beb9bb8a1ab139557baf0f4d367c;hp=37d63f2c57f6466d8675a0700b66c923f472e6ad;hb=6280ac9b987c14f89749b4b4fdfec5a647567432;hpb=4df593496e116dfb635731c058b7627e81fc179c diff --git a/src/generator.ml b/src/generator.ml index 37d63f2..20f7ac0 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -174,6 +174,14 @@ and argt = * To return an arbitrary buffer, use RBufferOut. *) | BufferIn of string + (* Key material / passphrase. Eventually we should treat this + * as sensitive and mlock it into physical RAM. However this + * is highly complex because of all the places that XDR-encoded + * strings can end up. So currently the only difference from + * 'String' is the way that guestfish requests these parameters + * from the user. + *) + | Key of string type flags = | ProtocolLimitWarning (* display warning about protocol size limits *) @@ -551,15 +559,13 @@ handle is closed. We don't currently have any method to enable changes to be committed, although qemu can support this. This is equivalent to the qemu parameter -C<-drive file=filename,snapshot=on,readonly=on,if=...>. +C<-drive file=filename,snapshot=on,if=...>. C is set at compile time by the configuration option C<./configure --with-drive-if=...>. In the rare case where you might need to change this at run time, use C or C. -C is only added where qemu supports this option. - Note that this call checks for the existence of C. This stops you from specifying other types of drive which are supported by qemu such as C and C URLs. To specify those, use @@ -1645,7 +1651,7 @@ the type or contents of the file. This call will also transparently look inside various types of compressed file. -The exact command which runs is C. Note in +The exact command which runs is C. Note in particular that the filename is not prepended to the output (the C<-b> option). @@ -4832,6 +4838,131 @@ C. If the filesystem does not have a UUID, this returns the empty string."); + ("lvm_set_filter", (RErr, [DeviceList "devices"]), 255, [Optional "lvm2"], + (* Can't be tested with the current framework because + * the VG is being used by the mounted filesystem, so + * the vgchange -an command we do first will fail. + *) + [], + "set LVM device filter", + "\ +This sets the LVM device filter so that LVM will only be +able to \"see\" the block devices in the list C, +and will ignore all other attached block devices. + +Where disk image(s) contain duplicate PVs or VGs, this +command is useful to get LVM to ignore the duplicates, otherwise +LVM can get confused. Note also there are two types +of duplication possible: either cloned PVs/VGs which have +identical UUIDs; or VGs that are not cloned but just happen +to have the same name. In normal operation you cannot +create this situation, but you can do it outside LVM, eg. +by cloning disk images or by bit twiddling inside the LVM +metadata. + +This command also clears the LVM cache and performs a volume +group scan. + +You can filter whole block devices or individual partitions. + +You cannot use this if any VG is currently in use (eg. +contains a mounted filesystem), even if you are not +filtering out that VG."); + + ("lvm_clear_filter", (RErr, []), 256, [], + [], (* see note on lvm_set_filter *) + "clear LVM device filter", + "\ +This undoes the effect of C. LVM +will be able to see every block device. + +This command also clears the LVM cache and performs a volume +group scan."); + + ("luks_open", (RErr, [Device "device"; Key "key"; String "mapname"]), 257, [Optional "luks"], + [], + "open a LUKS-encrypted block device", + "\ +This command opens a block device which has been encrypted +according to the Linux Unified Key Setup (LUKS) standard. + +C is the encrypted block device or partition. + +The caller must supply one of the keys associated with the +LUKS block device, in the C parameter. + +This creates a new block device called C. +Reads and writes to this block device are decrypted from and +encrypted to the underlying C respectively. + +If this block device contains LVM volume groups, then +calling C followed by C +will make them visible."); + + ("luks_open_ro", (RErr, [Device "device"; Key "key"; String "mapname"]), 258, [Optional "luks"], + [], + "open a LUKS-encrypted block device read-only", + "\ +This is the same as C except that a read-only +mapping is created."); + + ("luks_close", (RErr, [Device "device"]), 259, [Optional "luks"], + [], + "close a LUKS device", + "\ +This closes a LUKS device that was created earlier by +C or C. The +C parameter must be the name of the LUKS mapping +device (ie. C) and I the name +of the underlying block device."); + + ("luks_format", (RErr, [Device "device"; Key "key"; Int "keyslot"]), 260, [Optional "luks"; DangerWillRobinson], + [], + "format a block device as a LUKS encrypted device", + "\ +This command erases existing data on C and formats +the device as a LUKS encrypted device. C is the +initial key, which is added to key slot C. (LUKS +supports 8 key slots, numbered 0-7)."); + + ("luks_format_cipher", (RErr, [Device "device"; Key "key"; Int "keyslot"; String "cipher"]), 261, [Optional "luks"; DangerWillRobinson], + [], + "format a block device as a LUKS encrypted device", + "\ +This command is the same as C but +it also allows you to set the C used."); + + ("luks_add_key", (RErr, [Device "device"; Key "key"; Key "newkey"; Int "keyslot"]), 262, [Optional "luks"], + [], + "add a key on a LUKS encrypted device", + "\ +This command adds a new key on LUKS device C. +C is any existing key, and is used to access the device. +C is the new key to add. C is the key slot +that will be replaced. + +Note that if C already contains a key, then this +command will fail. You have to use C +first to remove that key."); + + ("luks_kill_slot", (RErr, [Device "device"; Key "key"; Int "keyslot"]), 263, [Optional "luks"], + [], + "remove a key from a LUKS encrypted device", + "\ +This command deletes the key in key slot C from the +encrypted LUKS device C. C must be one of the +I keys."); + + ("is_lv", (RBool "lvflag", [Device "device"]), 264, [Optional "lvm2"], + [InitBasicFSonLVM, IfAvailable "lvm2", TestOutputTrue ( + [["is_lv"; "/dev/VG/LV"]]); + InitBasicFSonLVM, IfAvailable "lvm2", TestOutputFalse ( + [["is_lv"; "/dev/sda1"]])], + "test if device is a logical volume", + "\ +This command tests whether C is a logical volume, and +returns true iff this is the case."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -5253,7 +5384,7 @@ let map_chars f str = let name_of_argt = function | Pathname n | Device n | Dev_or_Path n | String n | OptString n | StringList n | DeviceList n | Bool n | Int n | Int64 n - | FileIn n | FileOut n | BufferIn n -> n + | FileIn n | FileOut n | BufferIn n | Key n -> n let java_name_of_struct typ = try List.assoc typ java_structs @@ -5614,6 +5745,10 @@ I.\n\n" pr "%s\n\n" protocol_limit_warning; if List.mem DangerWillRobinson flags then pr "%s\n\n" danger_will_robinson; + if List.exists (function Key _ -> true | _ -> false) (snd style) then + pr "This function takes a key or passphrase parameter which +could contain sensitive material. Read the section +L for more information.\n\n"; match deprecation_notice flags with | None -> () | Some txt -> pr "%s\n\n" txt @@ -5719,7 +5854,7 @@ and generate_xdr () = pr "struct %s_args {\n" name; List.iter ( function - | Pathname n | Device n | Dev_or_Path n | String n -> + | Pathname n | Device n | Dev_or_Path n | String n | Key n -> pr " string %s<>;\n" n | OptString n -> pr " str *%s;\n" n | StringList n | DeviceList n -> pr " str %s<>;\n" n @@ -6005,7 +6140,8 @@ check_state (guestfs_h *g, const char *caller) | FileOut n | BufferIn n | StringList n - | DeviceList n -> + | DeviceList n + | Key n -> pr " if (%s == NULL) {\n" n; pr " error (g, \"%%s: %%s: parameter cannot be NULL\",\n"; pr " \"%s\", \"%s\");\n" shortname n; @@ -6034,7 +6170,7 @@ check_state (guestfs_h *g, const char *caller) | StringList _ | DeviceList _ -> true | _ -> false) (snd style) in if needs_i then ( - pr " int i;\n"; + pr " size_t i;\n"; pr "\n" ); @@ -6047,7 +6183,8 @@ check_state (guestfs_h *g, const char *caller) | Dev_or_Path n | FileIn n | FileOut n - | BufferIn n -> + | BufferIn n + | Key n -> (* guestfish doesn't support string escaping, so neither do we *) pr " printf (\" \\\"%%s\\\"\", %s);\n" n | OptString n -> (* string option *) @@ -6140,7 +6277,7 @@ check_state (guestfs_h *g, const char *caller) | args -> List.iter ( function - | Pathname n | Device n | Dev_or_Path n | String n -> + | Pathname n | Device n | Dev_or_Path n | String n | Key n -> pr " args.%s = (char *) %s;\n" n n | OptString n -> pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n @@ -6337,6 +6474,7 @@ and generate_linker_script () = "guestfs_get_error_handler"; "guestfs_get_out_of_memory_handler"; "guestfs_last_error"; + "guestfs_set_close_callback"; "guestfs_set_error_handler"; "guestfs_set_launch_done_callback"; "guestfs_set_log_message_callback"; @@ -6348,6 +6486,8 @@ and generate_linker_script () = *) "guestfs_safe_calloc"; "guestfs_safe_malloc"; + "guestfs_safe_strdup"; + "guestfs_safe_memdup"; ] in let functions = List.map (fun (name, _, _, _, _, _, _) -> "guestfs_" ^ name) @@ -6417,7 +6557,8 @@ and generate_daemon_actions () = function | Device n | Dev_or_Path n | Pathname n - | String n -> () + | String n + | Key n -> () | OptString n -> pr " char *%s;\n" n | StringList n | DeviceList n -> pr " char **%s;\n" n | Bool n -> pr " int %s;\n" n @@ -6474,16 +6615,19 @@ and generate_daemon_actions () = pr_args n; pr " REQUIRE_ROOT_OR_RESOLVE_DEVICE (%s, %s, goto done);\n" n (if is_filein then "cancel_receive ()" else "0"); - | String n -> pr_args n + | String n | Key n -> pr_args n | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n | StringList n -> pr_list_handling_code n; | DeviceList n -> pr_list_handling_code n; pr " /* Ensure that each is a device,\n"; - pr " * and perform device name translation. */\n"; - pr " { int pvi; for (pvi = 0; physvols[pvi] != NULL; ++pvi)\n"; - pr " RESOLVE_DEVICE (physvols[pvi], %s, goto done);\n" + pr " * and perform device name translation.\n"; + pr " */\n"; + pr " {\n"; + pr " size_t i;\n"; + pr " for (i = 0; %s[i] != NULL; ++i)\n" n; + pr " RESOLVE_DEVICE (%s[i], %s, goto done);\n" n (if is_filein then "cancel_receive ()" else "0"); pr " }\n"; | Bool n -> pr " %s = args.%s;\n" n n @@ -6631,7 +6775,7 @@ and generate_daemon_actions () = pr "static int lvm_tokenize_%s (char *str, guestfs_int_lvm_%s *r)\n" typ typ; pr "{\n"; pr " char *tok, *p, *next;\n"; - pr " int i, j;\n"; + pr " size_t i, j;\n"; pr "\n"; (* pr " fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n"; @@ -6856,7 +7000,7 @@ static void print_error (guestfs_h *g, void *data, const char *msg) /* FIXME: nearly identical code appears in fish.c */ static void print_strings (char *const *argv) { - int argc; + size_t argc; for (argc = 0; argv[argc] != NULL; ++argc) printf (\"\\t%%s\\n\", argv[argc]); @@ -6865,7 +7009,7 @@ static void print_strings (char *const *argv) /* static void print_table (char const *const *argv) { - int i; + size_t i; for (i = 0; argv[i] != NULL; i += 2) printf (\"%%s: %%s\\n\", argv[i], argv[i+1]); @@ -6885,6 +7029,13 @@ is_available (const char *group) return r == 0; } +static void +incr (guestfs_h *g, void *iv) +{ + int *i = (int *) iv; + (*i)++; +} + "; (* Generate a list of commands which are not tested anywhere. *) @@ -7066,11 +7217,22 @@ int main (int argc, char *argv[]) ) test_names; pr "\n"; - pr " guestfs_close (g);\n"; - pr " unlink (\"test1.img\");\n"; - pr " unlink (\"test2.img\");\n"; - pr " unlink (\"test3.img\");\n"; - pr "\n"; + pr " /* Check close callback is called. */ + int close_sentinel = 1; + guestfs_set_close_callback (g, incr, &close_sentinel); + + guestfs_close (g); + + if (close_sentinel != 2) { + fprintf (stderr, \"close callback was not called\\n\"); + exit (EXIT_FAILURE); + } + + unlink (\"test1.img\"); + unlink (\"test2.img\"); + unlink (\"test3.img\"); + +"; pr " if (n_failed > 0) {\n"; pr " printf (\"***** %%lu / %%d tests FAILED *****\\n\", n_failed, nr_tests);\n"; @@ -7468,7 +7630,8 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | Device n, arg | Dev_or_Path n, arg | String n, arg - | OptString n, arg -> + | OptString n, arg + | Key n, arg -> pr " const char *%s = \"%s\";\n" n (c_quote arg); | BufferIn n, arg -> pr " const char *%s = \"%s\";\n" n (c_quote arg); @@ -7502,7 +7665,7 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ | RHashtable _ -> pr " char **r;\n"; - pr " int i;\n"; + pr " size_t i;\n"; "NULL" | RStruct (_, typ) -> pr " struct guestfs_%s *r;\n" typ; "NULL" @@ -7523,7 +7686,8 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | Pathname n, _ | Device n, _ | Dev_or_Path n, _ | String n, _ - | OptString n, _ -> + | OptString n, _ + | Key n, _ -> pr ", %s" n | BufferIn n, _ -> pr ", %s, %s_size" n n @@ -7649,14 +7813,22 @@ and generate_fish_cmds () = match snd style with | [] -> name2 | args -> + let args = List.filter (function Key _ -> false | _ -> true) args in sprintf "%s %s" name2 (String.concat " " (List.map name_of_argt args)) in let warnings = - if List.mem ProtocolLimitWarning flags then - ("\n\n" ^ protocol_limit_warning) + if List.exists (function Key _ -> true | _ -> false) (snd style) then + "\n\nThis command has one or more key or passphrase parameters. +Guestfish will prompt for these separately." else "" in + let warnings = + warnings ^ + if List.mem ProtocolLimitWarning flags then + ("\n\n" ^ protocol_limit_warning) + else "" in + (* For DangerWillRobinson commands, we should probably have * guestfish prompt before allowing you to use them (especially * in interactive mode). XXX @@ -7814,7 +7986,8 @@ and generate_fish_cmds () = | Pathname n | Dev_or_Path n | FileIn n - | FileOut n -> pr " char *%s;\n" n + | FileOut n + | Key n -> pr " char *%s;\n" n | BufferIn n -> pr " const char *%s;\n" n; pr " size_t %s_size;\n" n @@ -7825,7 +7998,10 @@ and generate_fish_cmds () = ) (snd style); (* Check and convert parameters. *) - let argc_expected = List.length (snd style) in + let argc_expected = + let args_no_keys = + List.filter (function Key _ -> false | _ -> true) (snd style) in + List.length args_no_keys in pr " if (argc != %d) {\n" argc_expected; pr " fprintf (stderr, _(\"%%s should have %%d parameter(s)\\n\"), cmd, %d);\n" argc_expected; @@ -7833,12 +8009,12 @@ and generate_fish_cmds () = pr " return -1;\n"; pr " }\n"; - let parse_integer fn fntyp rtyp range name i = + let parse_integer fn fntyp rtyp range name = pr " {\n"; pr " strtol_error xerr;\n"; pr " %s r;\n" fntyp; pr "\n"; - pr " xerr = %s (argv[%d], NULL, 0, &r, xstrtol_suffixes);\n" fn i; + pr " xerr = %s (argv[i++], NULL, 0, &r, xstrtol_suffixes);\n" fn; pr " if (xerr != LONGINT_OK) {\n"; pr " fprintf (stderr,\n"; pr " _(\"%%s: %%s: invalid integer parameter (%%s returned %%d)\\n\"),\n"; @@ -7860,43 +8036,49 @@ and generate_fish_cmds () = pr " }\n"; in - iteri ( - fun i -> - function - | Device name - | String name -> - pr " %s = argv[%d];\n" name i - | Pathname name - | Dev_or_Path name -> - pr " %s = resolve_win_path (argv[%d]);\n" name i; - pr " if (%s == NULL) return -1;\n" name - | OptString name -> - pr " %s = STRNEQ (argv[%d], \"\") ? argv[%d] : NULL;\n" - name i i - | BufferIn name -> - pr " %s = argv[%d];\n" name i; - pr " %s_size = strlen (argv[%d]);\n" name i - | FileIn name -> - pr " %s = file_in (argv[%d]);\n" name i; - pr " if (%s == NULL) return -1;\n" name - | FileOut name -> - pr " %s = file_out (argv[%d]);\n" name i; - pr " if (%s == NULL) return -1;\n" name - | StringList name | DeviceList name -> - pr " %s = parse_string_list (argv[%d]);\n" name i; - pr " if (%s == NULL) return -1;\n" name; - | Bool name -> - pr " %s = is_true (argv[%d]) ? 1 : 0;\n" name i - | Int name -> - let range = - let min = "(-(2LL<<30))" - and max = "((2LL<<30)-1)" - and comment = - "The Int type in the generator is a signed 31 bit int." in - Some (min, max, comment) in - parse_integer "xstrtoll" "long long" "int" range name i - | Int64 name -> - parse_integer "xstrtoll" "long long" "int64_t" None name i + if snd style <> [] then + pr " size_t i = 0;\n"; + + List.iter ( + function + | Device name + | String name -> + pr " %s = argv[i++];\n" name + | Pathname name + | Dev_or_Path name -> + pr " %s = resolve_win_path (argv[i++]);\n" name; + pr " if (%s == NULL) return -1;\n" name + | OptString name -> + pr " %s = STRNEQ (argv[i], \"\") ? argv[i] : NULL;\n" name; + pr " i++;\n" + | BufferIn name -> + pr " %s = argv[i];\n" name; + pr " %s_size = strlen (argv[i]);\n" name; + pr " i++;\n" + | FileIn name -> + pr " %s = file_in (argv[i++]);\n" name; + pr " if (%s == NULL) return -1;\n" name + | FileOut name -> + pr " %s = file_out (argv[i++]);\n" name; + pr " if (%s == NULL) return -1;\n" name + | StringList name | DeviceList name -> + pr " %s = parse_string_list (argv[i++]);\n" name; + pr " if (%s == NULL) return -1;\n" name + | Key name -> + pr " %s = read_key (\"%s\");\n" name name; + pr " if (%s == NULL) return -1;\n" name + | Bool name -> + pr " %s = is_true (argv[i++]) ? 1 : 0;\n" name + | Int name -> + let range = + let min = "(-(2LL<<30))" + and max = "((2LL<<30)-1)" + and comment = + "The Int type in the generator is a signed 31 bit int." in + Some (min, max, comment) in + parse_integer "xstrtoll" "long long" "int" range name + | Int64 name -> + parse_integer "xstrtoll" "long long" "int64_t" None name ) (snd style); (* Call C API function. *) @@ -7906,11 +8088,12 @@ and generate_fish_cmds () = List.iter ( function - | Device name | String name - | OptString name | Bool name - | Int name | Int64 name - | BufferIn name -> () - | Pathname name | Dev_or_Path name | FileOut name -> + | Device _ | String _ + | OptString _ | Bool _ + | Int _ | Int64 _ + | BufferIn _ -> () + | Pathname name | Dev_or_Path name | FileOut name + | Key name -> pr " free (%s);\n" name | FileIn name -> pr " free_file_in (%s);\n" name @@ -8082,7 +8265,7 @@ static const char *const commands[] = { static char * generator (const char *text, int state) { - static int index, len; + static size_t index, len; const char *name; if (!state) { @@ -8163,7 +8346,8 @@ and generate_fish_actions_pod () = pr " %s" name; List.iter ( function - | Pathname n | Device n | Dev_or_Path n | String n -> pr " %s" n + | Pathname n | Device n | Dev_or_Path n | String n -> + pr " %s" n | OptString n -> pr " %s" n | StringList n | DeviceList n -> pr " '%s ...'" n | Bool _ -> pr " true|false" @@ -8171,6 +8355,7 @@ and generate_fish_actions_pod () = | Int64 n -> pr " %s" n | FileIn n | FileOut n -> pr " (%s|-)" n | BufferIn n -> pr " %s" n + | Key _ -> () (* keys are entered at a prompt *) ) (snd style); pr "\n"; pr "\n"; @@ -8180,6 +8365,10 @@ and generate_fish_actions_pod () = | _ -> false) (snd style) then pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n"; + if List.exists (function Key _ -> true | _ -> false) (snd style) then + pr "This command has one or more key or passphrase parameters. +Guestfish will prompt for these separately.\n\n"; + if List.mem ProtocolLimitWarning flags then pr "%s\n\n" protocol_limit_warning; @@ -8234,7 +8423,8 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) | Pathname n | Device n | Dev_or_Path n | String n - | OptString n -> + | OptString n + | Key n -> next (); pr "const char *%s" n | StringList n | DeviceList n -> @@ -8376,7 +8566,7 @@ and generate_ocaml_c () = #include #include -#include +#include \"guestfs.h\" #include \"guestfs_c.h\" @@ -8389,7 +8579,7 @@ copy_table (char * const * argv) { CAMLparam0 (); CAMLlocal5 (rv, pairv, kv, vv, cons); - int i; + size_t i; rv = Val_int (0); for (i = 0; argv[i] != NULL; i += 2) { @@ -8543,15 +8733,17 @@ copy_table (char * const * argv) | Device n | Dev_or_Path n | String n | FileIn n - | FileOut n -> - pr " const char *%s = String_val (%sv);\n" n n + | FileOut n + | Key n -> + (* Copy strings in case the GC moves them: RHBZ#604691 *) + pr " char *%s = guestfs_safe_strdup (g, String_val (%sv));\n" n n | OptString n -> - pr " const char *%s =\n" n; - pr " %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n" - n n + pr " char *%s =\n" n; + pr " %sv != Val_int (0) ?" n; + pr " guestfs_safe_strdup (g, String_val (Field (%sv, 0))) : NULL;\n" n | BufferIn n -> - pr " const char *%s = String_val (%sv);\n" n n; - pr " size_t %s_size = caml_string_length (%sv);\n" n n + pr " size_t %s_size = caml_string_length (%sv);\n" n n; + pr " char *%s = guestfs_safe_memdup (g, String_val (%sv), %s_size);\n" n n n | StringList n | DeviceList n -> pr " char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n | Bool n -> @@ -8571,7 +8763,7 @@ copy_table (char * const * argv) pr " const char *r;\n"; "NULL" | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ -> - pr " int i;\n"; + pr " size_t i;\n"; pr " char **r;\n"; "NULL" | RStruct (_, typ) -> @@ -8579,7 +8771,7 @@ copy_table (char * const * argv) | RStructList (_, typ) -> pr " struct guestfs_%s_list *r;\n" typ; "NULL" | RHashtable _ -> - pr " int i;\n"; + pr " size_t i;\n"; pr " char **r;\n"; "NULL" | RBufferOut _ -> @@ -8594,13 +8786,15 @@ copy_table (char * const * argv) pr ";\n"; pr " caml_leave_blocking_section ();\n"; + (* Free strings if we copied them above. *) List.iter ( function + | Pathname n | Device n | Dev_or_Path n | String n | OptString n + | FileIn n | FileOut n | BufferIn n | Key n -> + pr " free (%s);\n" n | StringList n | DeviceList n -> pr " ocaml_guestfs_free_strings (%s);\n" n; - | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ - | Bool _ | Int _ | Int64 _ - | FileIn _ | FileOut _ | BufferIn _ -> () + | Bool _ | Int _ | Int64 _ -> () ) (snd style); pr " if (r == %s)\n" error_code; @@ -8687,7 +8881,7 @@ and generate_ocaml_prototype ?(is_external = false) name style = List.iter ( function | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _ - | BufferIn _ -> pr "string -> " + | BufferIn _ | Key _ -> pr "string -> " | OptString _ -> pr "string option -> " | StringList _ | DeviceList _ -> pr "string array -> " | Bool _ -> pr "bool -> " @@ -8802,10 +8996,30 @@ _create () RETVAL void -DESTROY (g) +DESTROY (sv) + SV *sv; + PPCODE: + /* For the 'g' argument above we do the conversion explicitly and + * don't rely on the typemap, because if the handle has been + * explicitly closed we don't want the typemap conversion to + * display an error. + */ + HV *hv = (HV *) SvRV (sv); + SV **svp = hv_fetch (hv, \"_g\", 2, 0); + if (svp != NULL) { + guestfs_h *g = (guestfs_h *) SvIV (*svp); + assert (g != NULL); + guestfs_close (g); + } + +void +close (g) guestfs_h *g; PPCODE: guestfs_close (g); + /* Avoid double-free in DESTROY method. */ + HV *hv = (HV *) SvRV (ST(0)); + (void) hv_delete (hv, \"_g\", 2, G_DISCARD); "; @@ -8836,7 +9050,7 @@ DESTROY (g) fun i -> function | Pathname n | Device n | Dev_or_Path n | String n - | FileIn n | FileOut n -> + | FileIn n | FileOut n | Key n -> pr " char *%s;\n" n | BufferIn n -> pr " char *%s;\n" n; @@ -8859,7 +9073,7 @@ DESTROY (g) | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _ | Int64 _ | FileIn _ | FileOut _ - | BufferIn _ -> () + | BufferIn _ | Key _ -> () | StringList n | DeviceList n -> pr " free (%s);\n" n ) (snd style) in @@ -8947,7 +9161,7 @@ DESTROY (g) | RStringList n | RHashtable n -> pr "PREINIT:\n"; pr " char **%s;\n" n; - pr " int i, n;\n"; + pr " size_t i, n;\n"; pr " PPCODE:\n"; pr " %s = guestfs_%s " n name; generate_c_call_args ~handle:"g" style; @@ -8991,7 +9205,7 @@ DESTROY (g) and generate_perl_struct_list_code typ cols name style n do_cleanups = pr "PREINIT:\n"; pr " struct guestfs_%s_list *%s;\n" typ n; - pr " int i;\n"; + pr " size_t i;\n"; pr " HV *hv;\n"; pr " PPCODE:\n"; pr " %s = guestfs_%s " n name; @@ -9160,11 +9374,28 @@ sub new { my $proto = shift; my $class = ref ($proto) || $proto; - my $self = Sys::Guestfs::_create (); + my $g = Sys::Guestfs::_create (); + my $self = { _g => $g }; bless $self, $class; return $self; } +=item $h->close (); + +Explicitly close the guestfs handle. + +B You should not usually call this function. The handle will +be closed implicitly when its reference count goes to zero (eg. +when it goes out of scope or the program ends). This call is +only required in some exceptional cases, such as where the program +may contain cached references to the handle 'somewhere' and you +really have to have the close happen right away. After calling +C the program must not call any method (including C) +on the handle (but the implicit call to C that happens +when the final reference is cleaned up is OK). + +=cut + " max_proc_nr; (* Actions. We only need to print documentation for these as @@ -9238,7 +9469,7 @@ and generate_perl_prototype name style = match arg with | Pathname n | Device n | Dev_or_Path n | String n | OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n - | BufferIn n -> + | BufferIn n | Key n -> pr "$%s" n | StringList n | DeviceList n -> pr "\\@%s" n @@ -9290,7 +9521,7 @@ put_handle (guestfs_h *g) static char ** get_string_list (PyObject *obj) { - int i, len; + size_t i, len; char **r; assert (obj); @@ -9300,7 +9531,12 @@ get_string_list (PyObject *obj) return NULL; } - len = PyList_Size (obj); + Py_ssize_t slen = PyList_Size (obj); + if (slen == -1) { + PyErr_SetString (PyExc_RuntimeError, \"get_string_list: PyList_Size failure\"); + return NULL; + } + len = (size_t) slen; r = malloc (sizeof (char *) * (len+1)); if (r == NULL) { PyErr_SetString (PyExc_RuntimeError, \"get_string_list: out of memory\"); @@ -9398,7 +9634,7 @@ py_guestfs_close (PyObject *self, PyObject *args) pr "put_%s_list (struct guestfs_%s_list *%ss)\n" typ typ typ; pr "{\n"; pr " PyObject *list;\n"; - pr " int i;\n"; + pr " size_t i;\n"; pr "\n"; pr " list = PyList_New (%ss->len);\n" typ; pr " for (i = 0; i < %ss->len; ++i)\n" typ; @@ -9504,7 +9740,7 @@ py_guestfs_close (PyObject *self, PyObject *args) List.iter ( function - | Pathname n | Device n | Dev_or_Path n | String n + | Pathname n | Device n | Dev_or_Path n | String n | Key n | FileIn n | FileOut n -> pr " const char *%s;\n" n | OptString n -> pr " const char *%s;\n" n @@ -9525,7 +9761,8 @@ py_guestfs_close (PyObject *self, PyObject *args) pr " if (!PyArg_ParseTuple (args, (char *) \"O"; List.iter ( function - | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _ -> pr "s" + | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ + | FileIn _ | FileOut _ -> pr "s" | OptString _ -> pr "z" | StringList _ | DeviceList _ -> pr "O" | Bool _ -> pr "i" (* XXX Python has booleans? *) @@ -9539,7 +9776,8 @@ py_guestfs_close (PyObject *self, PyObject *args) pr " &py_g"; List.iter ( function - | Pathname n | Device n | Dev_or_Path n | String n | FileIn n | FileOut n -> pr ", &%s" n + | Pathname n | Device n | Dev_or_Path n | String n | Key n + | FileIn n | FileOut n -> pr ", &%s" n | OptString n -> pr ", &%s" n | StringList n | DeviceList n -> pr ", &py_%s" n | Bool n -> pr ", &%s" n @@ -9554,7 +9792,7 @@ py_guestfs_close (PyObject *self, PyObject *args) pr " g = get_handle (py_g);\n"; List.iter ( function - | Pathname _ | Device _ | Dev_or_Path _ | String _ + | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ | BufferIn _ -> () | StringList n | DeviceList n -> @@ -9570,7 +9808,7 @@ py_guestfs_close (PyObject *self, PyObject *args) List.iter ( function - | Pathname _ | Device _ | Dev_or_Path _ | String _ + | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ | BufferIn _ -> () | StringList n | DeviceList n -> @@ -9877,7 +10115,8 @@ static VALUE ruby_guestfs_close (VALUE gv) List.iter ( function - | Pathname n | Device n | Dev_or_Path n | String n | FileIn n | FileOut n -> + | Pathname n | Device n | Dev_or_Path n | String n | Key n + | FileIn n | FileOut n -> pr " Check_Type (%sv, T_STRING);\n" n; pr " const char *%s = StringValueCStr (%sv);\n" n n; pr " if (!%s)\n" n; @@ -9896,7 +10135,7 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " char **%s;\n" n; pr " Check_Type (%sv, T_ARRAY);\n" n; pr " {\n"; - pr " int i, len;\n"; + pr " size_t i, len;\n"; pr " len = RARRAY_LEN (%sv);\n" n; pr " %s = guestfs_safe_malloc (g, sizeof (char *) * (len+1));\n" n; @@ -9938,7 +10177,7 @@ static VALUE ruby_guestfs_close (VALUE gv) List.iter ( function - | Pathname _ | Device _ | Dev_or_Path _ | String _ + | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ | BufferIn _ -> () | StringList n | DeviceList n -> @@ -9968,7 +10207,7 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " free (r);\n"; pr " return rv;\n"; | RStringList _ -> - pr " int i, len = 0;\n"; + pr " size_t 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"; @@ -9985,7 +10224,7 @@ static VALUE ruby_guestfs_close (VALUE gv) generate_ruby_struct_list_code typ cols | RHashtable _ -> pr " VALUE rv = rb_hash_new ();\n"; - pr " int i;\n"; + pr " size_t 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"; @@ -10054,7 +10293,7 @@ and generate_ruby_struct_code typ cols = (* Ruby code to return a struct list. *) and generate_ruby_struct_list_code typ cols = pr " VALUE rv = rb_ary_new2 (r->len);\n"; - pr " int i;\n"; + pr " size_t i;\n"; pr " for (i = 0; i < r->len; ++i) {\n"; pr " VALUE hv = rb_hash_new ();\n"; List.iter ( @@ -10255,7 +10494,8 @@ and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false) | String n | OptString n | FileIn n - | FileOut n -> + | FileOut n + | Key n -> pr "String %s" n | BufferIn n -> pr "byte[] %s" n @@ -10378,7 +10618,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | String n | OptString n | FileIn n - | FileOut n -> + | FileOut n + | Key n -> pr ", jstring j%s" n | BufferIn n -> pr ", jbyteArray j%s" n @@ -10435,7 +10676,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | String n | OptString n | FileIn n - | FileOut n -> + | FileOut n + | Key n -> pr " const char *%s;\n" n | BufferIn n -> pr " jbyte *%s;\n" n; @@ -10461,7 +10703,7 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | DeviceList _ -> true | _ -> false) (snd style) in if needs_i then - pr " int i;\n"; + pr " size_t i;\n"; pr "\n"; @@ -10472,7 +10714,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | Device n | Dev_or_Path n | String n | FileIn n - | FileOut n -> + | FileOut n + | Key n -> pr " %s = (*env)->GetStringUTFChars (env, j%s, NULL);\n" n n | OptString n -> (* This is completely undocumented, but Java null becomes @@ -10509,7 +10752,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | Device n | Dev_or_Path n | String n | FileIn n - | FileOut n -> + | FileOut n + | Key n -> pr " (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n | OptString n -> pr " if (j%s)\n" n; @@ -10790,7 +11034,7 @@ last_error h = do function | FileIn n | FileOut n - | Pathname n | Device n | Dev_or_Path n | String n -> + | Pathname n | Device n | Dev_or_Path n | String n | Key n -> pr "withCString %s $ \\%s -> " n n | BufferIn n -> pr "withCStringLen %s $ \\(%s, %s_size) -> " n n n @@ -10806,7 +11050,10 @@ last_error h = do | Int n -> sprintf "(fromIntegral %s)" n | Int64 n -> sprintf "(fromIntegral %s)" n | FileIn n | FileOut n - | Pathname n | Device n | Dev_or_Path n | String n | OptString n | StringList n | DeviceList n -> n + | Pathname n | Device n | Dev_or_Path n + | String n | OptString n + | StringList n | DeviceList n + | Key n -> n | BufferIn n -> sprintf "%s (fromIntegral %s_size)" n n ) (snd style) in pr "withForeignPtr h (\\p -> c_%s %s)\n" name @@ -10857,7 +11104,8 @@ and generate_haskell_prototype ~handle ?(hs = false) style = List.iter ( fun arg -> (match arg with - | Pathname _ | Device _ | Dev_or_Path _ | String _ -> pr "%s" string + | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ -> + pr "%s" string | BufferIn _ -> if hs then pr "String" else pr "CString -> CInt" @@ -11051,6 +11299,7 @@ namespace Guestfs function | Pathname n | Device n | Dev_or_Path n | String n | OptString n | FileIn n | FileOut n + | Key n | BufferIn n -> pr ", [In] string %s" n | StringList n | DeviceList n -> @@ -11075,6 +11324,7 @@ namespace Guestfs function | Pathname n | Device n | Dev_or_Path n | String n | OptString n | FileIn n | FileOut n + | Key n | BufferIn n -> next (); pr "string %s" n | StringList n | DeviceList n -> @@ -11113,7 +11363,7 @@ namespace Guestfs pr " return r != 0 ? true : false;\n" | RHashtable _ -> pr " Hashtable rr = new Hashtable ();\n"; - pr " for (int i = 0; i < r.Length; i += 2)\n"; + pr " for (size_t i = 0; i < r.Length; i += 2)\n"; pr " rr.Add (r[i], r[i+1]);\n"; pr " return rr;\n" | RInt _ | RInt64 _ | RConstString _ | RConstOptString _ @@ -11150,7 +11400,7 @@ and generate_bindtests () = static void print_strings (char *const *argv) { - int argc; + size_t argc; printf (\"[\"); for (argc = 0; argv[argc] != NULL; ++argc) { @@ -11179,7 +11429,8 @@ print_strings (char *const *argv) | Device n | Dev_or_Path n | String n | FileIn n - | FileOut n -> pr " printf (\"%%s\\n\", %s);\n" n + | FileOut n + | Key n -> pr " printf (\"%%s\\n\", %s);\n" n | BufferIn n -> pr " {\n"; pr " size_t i;\n"; @@ -12112,8 +12363,8 @@ Run it from the top source directory using the command output_to "src/guestfs-structs.h" generate_structs_h; output_to "src/guestfs-actions.h" generate_actions_h; output_to "src/guestfs-internal-actions.h" generate_internal_actions_h; - output_to "src/guestfs-actions.c" generate_client_actions; - output_to "src/guestfs-bindtests.c" generate_bindtests; + output_to "src/actions.c" generate_client_actions; + output_to "src/bindtests.c" generate_bindtests; output_to "src/guestfs-structs.pod" generate_structs_pod; output_to "src/guestfs-actions.pod" generate_actions_pod; output_to "src/guestfs-availability.pod" generate_availability_pod;