X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fgenerator.ml;h=090c2800f01d0de81a03ffcdf60e9e6b471790fa;hp=b5b24cf1b9cb79e02485327c4180a0b59872706c;hb=bda6cf75f259992bcba23c3c4c2339c50552f491;hpb=68467cb1c2f8b1cc7844ed6512b7f76ef2aba1d1 diff --git a/src/generator.ml b/src/generator.ml index b5b24cf..090c280 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -164,9 +164,8 @@ and argt = *) | FileIn of string | FileOut of string -(* Not implemented: (* Opaque buffer which can contain arbitrary 8 bit data. - * In the C API, this is expressed as pair. + * In the C API, this is expressed as pair. * Most other languages have a string type which can contain * ASCII NUL. We use whatever type is appropriate for each * language. @@ -175,13 +174,11 @@ and argt = * To return an arbitrary buffer, use RBufferOut. *) | BufferIn of string -*) type flags = | ProtocolLimitWarning (* display warning about protocol size limits *) | DangerWillRobinson (* flags particularly dangerous commands *) | FishAlias of string (* provide an alias for this cmd in guestfish *) - | FishAction of string (* call this function in guestfish *) | FishOutput of fish_output_t (* how to display output in guestfish *) | NotInFish (* do not export via guestfish *) | NotInDocs (* do not add this function to documentation *) @@ -385,6 +382,7 @@ let test_all_args = [ Int64 "integer64"; FileIn "filein"; FileOut "fileout"; + BufferIn "bufferin"; ] let test_all_rets = [ @@ -447,7 +445,7 @@ You probably don't want to call this function.")] *) let non_daemon_functions = test_functions @ [ - ("launch", (RErr, []), -1, [FishAlias "run"; FishAction "launch"], + ("launch", (RErr, []), -1, [FishAlias "run"], [], "launch the qemu subprocess", "\ @@ -958,8 +956,15 @@ exist. The mounted filesystem is writable, if we have sufficient permissions on the underlying device. -The filesystem options C and C are set with this -call, in order to improve reliability."); +B +When you use this call, the filesystem options C and C +are set implicitly. This was originally done because we thought it +would improve reliability, but it turns out that I<-o sync> has a +very large negative performance impact and negligible effect on +reliability. Therefore we recommend that you avoid using +C in any code that needs performance, and instead +use C (use an empty string for the first +parameter if you don't want any options)."); ("sync", (RErr, []), 2, [], [ InitEmpty, Always, TestRun [["sync"]]], @@ -1984,7 +1989,10 @@ See also C, C."); InitISOFS, Always, TestOutput ( [["checksum"; "sha384"; "/known-3"]], "5fa7883430f357b5d7b7271d3a1d2872b51d73cba72731de6863d3dea55f30646af2799bef44d5ea776a5ec7941ac640"); InitISOFS, Always, TestOutput ( - [["checksum"; "sha512"; "/known-3"]], "2794062c328c6b216dca90443b7f7134c5f40e56bd0ed7853123275a09982a6f992e6ca682f9d2fba34a4c5e870d8fe077694ff831e3032a004ee077e00603f6")], + [["checksum"; "sha512"; "/known-3"]], "2794062c328c6b216dca90443b7f7134c5f40e56bd0ed7853123275a09982a6f992e6ca682f9d2fba34a4c5e870d8fe077694ff831e3032a004ee077e00603f6"); + (* Test for RHBZ#579608, absolute symbolic links. *) + InitISOFS, Always, TestOutput ( + [["checksum"; "sha512"; "/abssymlink"]], "5f57d0639bc95081c53afc63a449403883818edc64da48930ad6b1a4fb49be90404686877743fbcd7c99811f3def7df7bc22635c885c6a8cf79c806b43451c1a")], "compute MD5, SHAx or CRC checksum of file", "\ This call computes the MD5, SHAx or CRC checksum of the @@ -2026,9 +2034,13 @@ Compute the SHA512 hash (using the C program). =back -The checksum is returned as a printable string."); +The checksum is returned as a printable string. + +To get the checksum for a device, use C. + +To get the checksums for many files, use C."); - ("tar_in", (RErr, [FileIn "tarfile"; String "directory"]), 69, [], + ("tar_in", (RErr, [FileIn "tarfile"; Pathname "directory"]), 69, [], [InitBasicFS, Always, TestOutput ( [["tar_in"; "../images/helloworld.tar"; "/"]; ["cat"; "/hello"]], "hello\n")], @@ -2050,7 +2062,7 @@ it to local file C. To download a compressed tarball, use C or C."); - ("tgz_in", (RErr, [FileIn "tarball"; String "directory"]), 71, [], + ("tgz_in", (RErr, [FileIn "tarball"; Pathname "directory"]), 71, [], [InitBasicFS, Always, TestOutput ( [["tgz_in"; "../images/helloworld.tar.gz"; "/"]; ["cat"; "/hello"]], "hello\n")], @@ -2091,7 +2103,11 @@ mounts the filesystem with the read-only (I<-o ro>) flag."); "\ This is the same as the C command, but it allows you to set the mount options as for the -L I<-o> flag."); +L I<-o> flag. + +If the C parameter is an empty string, then +no options are passed (all options default to whatever +the filesystem uses)."); ("mount_vfs", (RErr, [String "options"; String "vfstype"; Device "device"; String "mountpoint"]), 75, [], [], @@ -2428,7 +2444,10 @@ The external L program is used for the comparison."); [InitISOFS, Always, TestOutputList ( [["strings"; "/known-5"]], ["abcdefghi"; "jklmnopqr"]); InitISOFS, Always, TestOutputList ( - [["strings"; "/empty"]], [])], + [["strings"; "/empty"]], []); + (* Test for RHBZ#579608, absolute symbolic links. *) + InitISOFS, Always, TestRun ( + [["strings"; "/abssymlink"]])], "print the printable strings in a file", "\ This runs the L command on a file and returns @@ -2443,12 +2462,41 @@ the list of printable strings found."); "print the printable strings in a file", "\ This is like the C command, but allows you to -specify the encoding. +specify the encoding of strings that are looked for in +the source file C. + +Allowed encodings are: + +=over 4 + +=item s + +Single 7-bit-byte characters like ASCII and the ASCII-compatible +parts of ISO-8859-X (this is what C uses). + +=item S + +Single 8-bit-byte characters. + +=item b -See the L manpage for the full list of encodings. +16-bit big endian strings such as those encoded in +UTF-16BE or UCS-2BE. -Commonly useful encodings are C (lower case L) which will -show strings inside Windows/x86 files. +=item l (lower case letter L) + +16-bit little endian such as UTF-16LE and UCS-2LE. +This is useful for examining binaries in Windows guests. + +=item B + +32-bit big endian such as UCS-4BE. + +=item L + +32-bit little endian such as UCS-4LE. + +=back The returned strings are transcoded to UTF-8."); @@ -2459,7 +2507,10 @@ The returned strings are transcoded to UTF-8."); * commands to segfault. *) InitISOFS, Always, TestRun ( - [["hexdump"; "/100krandom"]])], + [["hexdump"; "/100krandom"]]); + (* Test for RHBZ#579608, absolute symbolic links. *) + InitISOFS, Always, TestRun ( + [["hexdump"; "/abssymlink"]])], "dump a file in hexadecimal", "\ This runs C on the given C. The result is @@ -2581,7 +2632,14 @@ are activated or deactivated."); ["e2fsck_f"; "/dev/VG/LV"]; ["resize2fs"; "/dev/VG/LV"]; ["mount_options"; ""; "/dev/VG/LV"; "/"]; - ["cat"; "/new"]], "test content")], + ["cat"; "/new"]], "test content"); + InitNone, Always, TestRun ( + (* Make an LV smaller to test RHBZ#587484. *) + [["part_disk"; "/dev/sda"; "mbr"]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV"; "VG"; "20"]; + ["lvresize"; "/dev/VG/LV"; "10"]])], "resize an LVM logical volume", "\ This resizes (expands or shrinks) an existing LVM logical @@ -2807,7 +2865,10 @@ See also: L"); ("wc_l", (RInt "lines", [Pathname "path"]), 118, [], [InitISOFS, Always, TestOutputInt ( - [["wc_l"; "/10klines"]], 10000)], + [["wc_l"; "/10klines"]], 10000); + (* Test for RHBZ#579608, absolute symbolic links. *) + InitISOFS, Always, TestOutputInt ( + [["wc_l"; "/abssymlink"]], 10000)], "count lines in a file", "\ This command counts the lines in a file, using the @@ -2831,7 +2892,10 @@ C external command."); ("head", (RStringList "lines", [Pathname "path"]), 121, [ProtocolLimitWarning], [InitISOFS, Always, TestOutputList ( - [["head"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz";"3abcdefghijklmnopqrstuvwxyz";"4abcdefghijklmnopqrstuvwxyz";"5abcdefghijklmnopqrstuvwxyz";"6abcdefghijklmnopqrstuvwxyz";"7abcdefghijklmnopqrstuvwxyz";"8abcdefghijklmnopqrstuvwxyz";"9abcdefghijklmnopqrstuvwxyz"])], + [["head"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz";"3abcdefghijklmnopqrstuvwxyz";"4abcdefghijklmnopqrstuvwxyz";"5abcdefghijklmnopqrstuvwxyz";"6abcdefghijklmnopqrstuvwxyz";"7abcdefghijklmnopqrstuvwxyz";"8abcdefghijklmnopqrstuvwxyz";"9abcdefghijklmnopqrstuvwxyz"]); + (* Test for RHBZ#579608, absolute symbolic links. *) + InitISOFS, Always, TestOutputList ( + [["head"; "/abssymlink"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz";"3abcdefghijklmnopqrstuvwxyz";"4abcdefghijklmnopqrstuvwxyz";"5abcdefghijklmnopqrstuvwxyz";"6abcdefghijklmnopqrstuvwxyz";"7abcdefghijklmnopqrstuvwxyz";"8abcdefghijklmnopqrstuvwxyz";"9abcdefghijklmnopqrstuvwxyz"])], "return first 10 lines of a file", "\ This command returns up to the first 10 lines of a file as @@ -2989,6 +3053,14 @@ constants. C and C are the device major and minor numbers, only used when creating block and character special devices. +Note that, just like L, the mode must be bitwise +OR'd with S_IFBLK, S_IFCHR, S_IFIFO or S_IFSOCK (otherwise this call +just creates a regular file). These constants are +available in the standard Linux header files, or you can use +C, C or C +which are wrappers around this command which bitwise OR +in the appropriate constant for you. + The mode actually set is affected by the umask."); ("mkfifo", (RErr, [Int "mode"; Pathname "path"]), 134, [Optional "mknod"], @@ -3242,7 +3314,20 @@ for full details."); ("read_file", (RBufferOut "content", [Pathname "path"]), 150, [ProtocolLimitWarning], [InitISOFS, Always, TestOutputBuffer ( - [["read_file"; "/known-4"]], "abc\ndef\nghi")], + [["read_file"; "/known-4"]], "abc\ndef\nghi"); + (* Test various near large, large and too large files (RHBZ#589039). *) + InitBasicFS, Always, TestLastFail ( + [["touch"; "/a"]; + ["truncate_size"; "/a"; "4194303"]; (* GUESTFS_MESSAGE_MAX - 1 *) + ["read_file"; "/a"]]); + InitBasicFS, Always, TestLastFail ( + [["touch"; "/a"]; + ["truncate_size"; "/a"; "4194304"]; (* GUESTFS_MESSAGE_MAX *) + ["read_file"; "/a"]]); + InitBasicFS, Always, TestLastFail ( + [["touch"; "/a"]; + ["truncate_size"; "/a"; "41943040"]; (* GUESTFS_MESSAGE_MAX * 10 *) + ["read_file"; "/a"]])], "read a file", "\ This calls returns the contents of the file C as a @@ -3257,7 +3342,10 @@ in the total size of file that can be handled."); [InitISOFS, Always, TestOutputList ( [["grep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"]); InitISOFS, Always, TestOutputList ( - [["grep"; "nomatch"; "/test-grep.txt"]], [])], + [["grep"; "nomatch"; "/test-grep.txt"]], []); + (* Test for RHBZ#579608, absolute symbolic links. *) + InitISOFS, Always, TestOutputList ( + [["grep"; "nomatch"; "/abssymlink"]], [])], "return lines matching a pattern", "\ This calls the external C program and returns the @@ -4192,7 +4280,9 @@ content of the file is C octets of C, where C must be a number in the range C<[0..255]>. To fill a file with zero bytes (sparsely), it is -much more efficient to use C."); +much more efficient to use C. +To create a file with a pattern of repeating bytes +use C."); ("available", (RErr, [StringList "groups"]), 216, [], [InitNone, Always, TestRun [["available"; ""]]], @@ -4382,7 +4472,7 @@ This command writes zeroes over the entire C. Compare with C which just zeroes the first few blocks of a device."); - ("txz_in", (RErr, [FileIn "tarball"; String "directory"]), 229, [], + ("txz_in", (RErr, [FileIn "tarball"; Pathname "directory"]), 229, [], [InitBasicFS, Always, TestOutput ( [["txz_in"; "../images/helloworld.tar.xz"; "/"]; ["cat"; "/hello"]], "hello\n")], @@ -4509,6 +4599,60 @@ is the same as the L C command."); Return the current umask. By default the umask is C<022> unless it has been set by calling C."); + ("debug_upload", (RErr, [FileIn "filename"; String "tmpname"; Int "mode"]), 241, [], + [], + "upload a file to the appliance (internal use only)", + "\ +The C command uploads a file to +the libguestfs appliance. + +There is no comprehensive help for this command. You have +to look at the file C in the libguestfs source +to find out what it is for."); + + ("base64_in", (RErr, [FileIn "base64file"; Pathname "filename"]), 242, [], + [InitBasicFS, Always, TestOutput ( + [["base64_in"; "../images/hello.b64"; "/hello"]; + ["cat"; "/hello"]], "hello\n")], + "upload base64-encoded data to file", + "\ +This command uploads base64-encoded data from C +to C."); + + ("base64_out", (RErr, [Pathname "filename"; FileOut "base64file"]), 243, [], + [], + "download file and encode as base64", + "\ +This command downloads the contents of C, writing +it out to local file C encoded as base64."); + + ("checksums_out", (RErr, [String "csumtype"; Pathname "directory"; FileOut "sumsfile"]), 244, [], + [], + "compute MD5, SHAx or CRC checksum of files in a directory", + "\ +This command computes the checksums of all regular files in +C and then emits a list of those checksums to +the local output file C. + +This can be used for verifying the integrity of a virtual +machine. However to be properly secure you should pay +attention to the output of the checksum command (it uses +the ones from GNU coreutils). In particular when the +filename is not printable, coreutils uses a special +backslash syntax. For more information, see the GNU +coreutils info file."); + + ("fill_pattern", (RErr, [String "pattern"; Int "len"; Pathname "path"]), 245, [], + [InitBasicFS, Always, TestOutputBuffer ( + [["fill_pattern"; "abcdefghijklmnopqrstuvwxyz"; "28"; "/test"]; + ["read_file"; "/test"]], "abcdefghijklmnopqrstuvwxyzab")], + "fill a file with a repeating pattern of bytes", + "\ +This function is like C except that it creates +a new file of length C containing the repeating pattern +of bytes in C. The pattern is truncated if necessary +to ensure the length of the file is exactly C bytes."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -4520,6 +4664,16 @@ let all_functions_sorted = List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) -> compare n1 n2) all_functions +(* This is used to generate the src/MAX_PROC_NR file which + * contains the maximum procedure number, a surrogate for the + * ABI version number. See src/Makefile.am for the details. + *) +let max_proc_nr = + let proc_nrs = List.map ( + fun (_, _, proc_nr, _, _, _, _) -> proc_nr + ) daemon_functions in + List.fold_left max 0 proc_nrs + (* Field types for structures. *) type field = | FChar (* C 'char' (really, a 7 bit byte). *) @@ -4760,6 +4914,7 @@ type callt = | CallInt of int | CallInt64 of int64 | CallBool of bool + | CallBuffer of string (* Used to memoize the result of pod2text. *) let pod2text_memo_filename = "src/.pod2text.data" @@ -4905,10 +5060,21 @@ let count_chars c str = done; !count +let explode str = + let r = ref [] in + for i = 0 to String.length str - 1 do + let c = String.unsafe_get str i in + r := c :: !r; + done; + List.rev !r + +let map_chars f str = + List.map f (explode 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 -> n + | FileIn n | FileOut n | BufferIn n -> n let java_name_of_struct typ = try List.assoc typ java_structs @@ -5219,7 +5385,7 @@ let rec generate_actions_pod () = let name = "guestfs_" ^ shortname in pr "=head2 %s\n\n" name; pr " "; - generate_prototype ~extern:false ~handle:"handle" name style; + generate_prototype ~extern:false ~handle:"g" name style; pr "\n\n"; pr "%s\n\n" longdesc; (match fst style with @@ -5381,6 +5547,8 @@ and generate_xdr () = | Bool n -> pr " bool %s;\n" n | Int n -> pr " int %s;\n" n | Int64 n -> pr " hyper %s;\n" n + | BufferIn n -> + pr " opaque %s<>;\n" n | FileIn _ | FileOut _ -> () ) args; pr "};\n\n" @@ -5547,7 +5715,7 @@ and generate_actions_h () = List.iter ( fun (shortname, style, _, _, _, _, _) -> let name = "guestfs_" ^ shortname in - generate_prototype ~single_line:true ~newline:true ~handle:"handle" + generate_prototype ~single_line:true ~newline:true ~handle:"g" name style ) all_functions @@ -5557,7 +5725,7 @@ and generate_internal_actions_h () = List.iter ( fun (shortname, style, _, _, _, _, _) -> let name = "guestfs__" ^ shortname in - generate_prototype ~single_line:true ~newline:true ~handle:"handle" + generate_prototype ~single_line:true ~newline:true ~handle:"g" name style ) non_daemon_functions @@ -5655,7 +5823,8 @@ check_state (guestfs_h *g, const char *caller) | Pathname n | Dev_or_Path n | FileIn n - | FileOut n -> + | FileOut n + | BufferIn n -> (* guestfish doesn't support string escaping, so neither do we *) pr " printf (\" \\\"%%s\\\"\", %s);\n" n | OptString n -> (* string option *) @@ -5742,7 +5911,8 @@ check_state (guestfs_h *g, const char *caller) pr " int r;\n"; pr "\n"; trace_call shortname style; - pr " if (check_state (g, \"%s\") == -1) return %s;\n" name error_code; + pr " if (check_state (g, \"%s\") == -1) return %s;\n" + shortname error_code; pr " guestfs___set_busy (g);\n"; pr "\n"; @@ -5768,6 +5938,16 @@ check_state (guestfs_h *g, const char *caller) | Int64 n -> pr " args.%s = %s;\n" n n | FileIn _ | FileOut _ -> () + | BufferIn n -> + pr " /* Just catch grossly large sizes. XDR encoding will make this precise. */\n"; + pr " if (%s_size >= GUESTFS_MESSAGE_MAX) {\n" n; + pr " error (g, \"%%s: size of input buffer too large\", \"%s\");\n" + shortname; + pr " guestfs___end_busy (g);\n"; + pr " return %s;\n" error_code; + pr " }\n"; + pr " args.%s.%s_val = (char *) %s;\n" n n n; + pr " args.%s.%s_len = %s_size;\n" n n n ) args; pr " serial = guestfs___send (g, GUESTFS_PROC_%s,\n" (String.uppercase shortname); @@ -6028,18 +6208,26 @@ and generate_daemon_actions () = | Int n -> pr " int %s;\n" n | Int64 n -> pr " int64_t %s;\n" n | FileIn _ | FileOut _ -> () + | BufferIn n -> + pr " const char *%s;\n" n; + pr " size_t %s_size;\n" n ) args ); pr "\n"; + let is_filein = + List.exists (function FileIn _ -> true | _ -> false) (snd style) in + (match snd style with | [] -> () | args -> pr " memset (&args, 0, sizeof args);\n"; pr "\n"; pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name; - pr " reply_with_error (\"daemon failed to decode procedure arguments\");\n"; - pr " return;\n"; + if is_filein then + pr " if (cancel_receive () != -2)\n"; + pr " reply_with_error (\"daemon failed to decode procedure arguments\");\n"; + pr " goto done;\n"; pr " }\n"; let pr_args n = pr " char *%s = args.%s;\n" n n @@ -6048,7 +6236,9 @@ and generate_daemon_actions () = pr " %s = realloc (args.%s.%s_val,\n" n n n; pr " sizeof (char *) * (args.%s.%s_len+1));\n" n n; pr " if (%s == NULL) {\n" n; - pr " reply_with_perror (\"realloc\");\n"; + if is_filein then + pr " if (cancel_receive () != -2)\n"; + pr " reply_with_perror (\"realloc\");\n"; pr " goto done;\n"; pr " }\n"; pr " %s[args.%s.%s_len] = NULL;\n" n n n; @@ -6058,13 +6248,16 @@ and generate_daemon_actions () = function | Pathname n -> pr_args n; - pr " ABS_PATH (%s, goto done);\n" n; + pr " ABS_PATH (%s, %s, goto done);\n" + n (if is_filein then "cancel_receive ()" else "0"); | Device n -> pr_args n; - pr " RESOLVE_DEVICE (%s, goto done);\n" n; + pr " RESOLVE_DEVICE (%s, %s, goto done);\n" + n (if is_filein then "cancel_receive ()" else "0"); | Dev_or_Path n -> pr_args n; - pr " REQUIRE_ROOT_OR_RESOLVE_DEVICE (%s, goto done);\n" 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 | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n | StringList n -> @@ -6074,22 +6267,26 @@ and generate_daemon_actions () = 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], goto done);\n"; + pr " RESOLVE_DEVICE (physvols[pvi], %s, goto done);\n" + (if is_filein then "cancel_receive ()" else "0"); pr " }\n"; | Bool n -> pr " %s = args.%s;\n" n n | Int n -> pr " %s = args.%s;\n" n n | Int64 n -> pr " %s = args.%s;\n" n n | FileIn _ | FileOut _ -> () + | BufferIn n -> + pr " %s = args.%s.%s_val;\n" n n n; + pr " %s_size = args.%s.%s_len;\n" n n n ) args; pr "\n" ); - (* this is used at least for do_equal *) if List.exists (function Pathname _ -> true | _ -> false) (snd style) then ( (* Emit NEED_ROOT just once, even when there are two or more Pathname args *) - pr " NEED_ROOT (goto done);\n"; + pr " NEED_ROOT (%s, goto done);\n" + (if is_filein then "cancel_receive ()" else "0"); ); (* Don't want to call the impl with any FileIn or FileOut @@ -6175,15 +6372,14 @@ and generate_daemon_actions () = ); (* Free the args. *) + pr "done:\n"; (match snd style with - | [] -> - pr "done: ;\n"; + | [] -> () | _ -> - pr "done:\n"; pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n" name ); - + pr " return;\n"; pr "}\n\n"; ) daemon_functions; @@ -6631,6 +6827,8 @@ int main (int argc, char *argv[]) iteri ( fun i test_name -> pr " test_num++;\n"; + pr " if (guestfs_get_verbose (g))\n"; + pr " printf (\"-------------------------------------------------------------------------------\\n\");\n"; pr " printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name; pr " if (%s () == -1) {\n" test_name; pr " printf (\"%s FAILED\\n\");\n" test_name; @@ -7043,6 +7241,9 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | String n, arg | OptString 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); + pr " size_t %s_size = %d;\n" n (String.length arg) | Int _, _ | Int64 _, _ | Bool _, _ @@ -7095,6 +7296,8 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | String n, _ | OptString n, _ -> pr ", %s" n + | BufferIn n, _ -> + pr ", %s, %s_size" n n | FileIn _, arg | FileOut _, arg -> pr ", \"%s\"" (c_quote arg) | StringList n, _ | DeviceList n, _ -> @@ -7373,11 +7576,14 @@ and generate_fish_cmds () = function | Device n | String n - | OptString n - | FileIn n - | FileOut n -> pr " const char *%s;\n" n + | OptString n -> pr " const char *%s;\n" n | Pathname n - | Dev_or_Path n -> pr " char *%s;\n" n + | Dev_or_Path n + | FileIn n + | FileOut n -> pr " char *%s;\n" n + | BufferIn n -> + pr " const char *%s;\n" n; + pr " size_t %s_size;\n" n | StringList n | DeviceList n -> pr " char **%s;\n" n | Bool n -> pr " int %s;\n" n | Int n -> pr " int %s;\n" n @@ -7433,12 +7639,15 @@ and generate_fish_cmds () = | 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 = STRNEQ (argv[%d], \"-\") ? argv[%d] : \"/dev/stdin\";\n" - name i i + pr " %s = file_in (argv[%d]);\n" name i; + pr " if (%s == NULL) return -1;\n" name | FileOut name -> - pr " %s = STRNEQ (argv[%d], \"-\") ? argv[%d] : \"/dev/stdout\";\n" - name i i + 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; @@ -7457,20 +7666,20 @@ and generate_fish_cmds () = ) (snd style); (* Call C API function. *) - let fn = - try find_map (function FishAction n -> Some n | _ -> None) flags - with Not_found -> sprintf "guestfs_%s" name in - pr " r = %s " fn; + pr " r = guestfs_%s " name; generate_c_call_args ~handle:"g" style; pr ";\n"; List.iter ( function | Device name | String name - | OptString name | FileIn name | FileOut name | Bool name - | Int name | Int64 name -> () - | Pathname name | Dev_or_Path name -> + | OptString name | Bool name + | Int name | Int64 name + | BufferIn name -> () + | Pathname name | Dev_or_Path name | FileOut name -> pr " free (%s);\n" name + | FileIn name -> + pr " free_file_in (%s);\n" name | StringList name | DeviceList name -> pr " free_strings (%s);\n" name ) (snd style); @@ -7727,6 +7936,7 @@ and generate_fish_actions_pod () = | Int n -> pr " %s" n | Int64 n -> pr " %s" n | FileIn n | FileOut n -> pr " (%s|-)" n + | BufferIn n -> pr " %s" n ) (snd style); pr "\n"; pr "\n"; @@ -7802,6 +8012,11 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) | FileIn n | FileOut n -> if not in_daemon then (next (); pr "const char *%s" n) + | BufferIn n -> + next (); + pr "const char *%s" n; + next (); + pr "size_t %s_size" n ) (snd style); if is_RBufferOut then (next (); pr "size_t *size_r"); ); @@ -7822,9 +8037,13 @@ and generate_c_call_args ?handle ?(decl = false) style = | Some handle -> pr "%s" handle; comma := true ); List.iter ( - fun arg -> - next (); - pr "%s" (name_of_argt arg) + function + | BufferIn n -> + next (); + pr "%s, %s_size" n n + | arg -> + next (); + pr "%s" (name_of_argt arg) ) (snd style); (* For RBufferOut calls, add implicit &size parameter. *) if not decl then ( @@ -8096,6 +8315,9 @@ copy_table (char * const * argv) pr " const char *%s =\n" n; pr " %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n" 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 | StringList n | DeviceList n -> pr " char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n | Bool n -> @@ -8144,7 +8366,7 @@ copy_table (char * const * argv) pr " ocaml_guestfs_free_strings (%s);\n" n; | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _ | Int64 _ - | FileIn _ | FileOut _ -> () + | FileIn _ | FileOut _ | BufferIn _ -> () ) (snd style); pr " if (r == %s)\n" error_code; @@ -8230,7 +8452,8 @@ and generate_ocaml_prototype ?(is_external = false) name style = pr "%s : t -> " name; List.iter ( function - | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _ -> pr "string -> " + | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _ + | BufferIn _ -> pr "string -> " | OptString _ -> pr "string option -> " | StringList _ | DeviceList _ -> pr "string array -> " | Bool _ -> pr "bool -> " @@ -8369,15 +8592,21 @@ DESTROY (g) pr "void\n" (* all lists returned implictly on the stack *) ); (* Call and arguments. *) - pr "%s " name; - generate_c_call_args ~handle:"g" ~decl:true style; - pr "\n"; + pr "%s (g" name; + List.iter ( + fun arg -> pr ", %s" (name_of_argt arg) + ) (snd style); + pr ")\n"; pr " guestfs_h *g;\n"; iteri ( fun i -> 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 + | FileIn n | FileOut n -> pr " char *%s;\n" n + | BufferIn n -> + pr " char *%s;\n" n; + pr " size_t %s_size = SvCUR (ST(%d));\n" n (i+1) | OptString n -> (* http://www.perlmonks.org/?node_id=554277 * Note that the implicit handle argument means we have @@ -8395,7 +8624,8 @@ DESTROY (g) function | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _ | Int64 _ - | FileIn _ | FileOut _ -> () + | FileIn _ | FileOut _ + | BufferIn _ -> () | StringList n | DeviceList n -> pr " free (%s);\n" n ) (snd style) in @@ -8677,6 +8907,12 @@ package Sys::Guestfs; use strict; use warnings; +# This version number changes whenever a new function +# is added to the libguestfs API. It is not directly +# related to the libguestfs version number. +use vars qw($VERSION); +$VERSION = '0.%d'; + require XSLoader; XSLoader::load ('Sys::Guestfs'); @@ -8695,7 +8931,7 @@ sub new { return $self; } -"; +" max_proc_nr; (* Actions. We only need to print documentation for these as * they are pulled in from the XS code automatically. @@ -8767,7 +9003,8 @@ and generate_perl_prototype name style = comma := true; 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 -> + | OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n + | BufferIn n -> pr "$%s" n | StringList n | DeviceList n -> pr "\\@%s" n @@ -8779,6 +9016,7 @@ and generate_python_c () = generate_header CStyle LGPLv2plus; pr "\ +#define PY_SSIZE_T_CLEAN 1 #include #include @@ -9026,9 +9264,13 @@ py_guestfs_close (PyObject *self, PyObject *args) 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 + | FileIn n | FileOut n -> pr " const char *%s;\n" n | OptString n -> pr " const char *%s;\n" n + | BufferIn n -> + pr " const char *%s;\n" n; + pr " Py_ssize_t %s_size;\n" n | StringList n | DeviceList n -> pr " PyObject *py_%s;\n" n; pr " char **%s;\n" n @@ -9051,6 +9293,7 @@ py_guestfs_close (PyObject *self, PyObject *args) | Int64 _ -> pr "L" (* XXX Whoever thought it was a good idea to * emulate C's int/long/long long in Python? *) + | BufferIn _ -> pr "s#" ) (snd style); pr ":guestfs_%s\",\n" name; pr " &py_g"; @@ -9062,6 +9305,7 @@ py_guestfs_close (PyObject *self, PyObject *args) | Bool n -> pr ", &%s" n | Int n -> pr ", &%s" n | Int64 n -> pr ", &%s" n + | BufferIn n -> pr ", &%s, &%s_size" n n ) (snd style); pr "))\n"; @@ -9071,7 +9315,8 @@ py_guestfs_close (PyObject *self, PyObject *args) List.iter ( function | Pathname _ | Device _ | Dev_or_Path _ | String _ - | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> () + | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ + | BufferIn _ -> () | StringList n | DeviceList n -> pr " %s = get_string_list (py_%s);\n" n n; pr " if (!%s) return NULL;\n" n @@ -9086,7 +9331,8 @@ py_guestfs_close (PyObject *self, PyObject *args) List.iter ( function | Pathname _ | Device _ | Dev_or_Path _ | String _ - | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> () + | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ + | BufferIn _ -> () | StringList n | DeviceList n -> pr " free (%s);\n" n ) (snd style); @@ -9397,6 +9643,13 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " if (!%s)\n" n; pr " rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n"; pr " \"%s\", \"%s\");\n" n name + | BufferIn n -> + pr " Check_Type (%sv, T_STRING);\n" n; + pr " const char *%s = RSTRING (%sv)->ptr;\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; + pr " size_t %s_size = RSTRING (%sv)->len;\n" n n | OptString n -> pr " const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n | StringList n | DeviceList n -> @@ -9446,7 +9699,8 @@ static VALUE ruby_guestfs_close (VALUE gv) List.iter ( function | Pathname _ | Device _ | Dev_or_Path _ | String _ - | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> () + | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ + | BufferIn _ -> () | StringList n | DeviceList n -> pr " free (%s);\n" n ) (snd style); @@ -9763,6 +10017,8 @@ and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false) | FileIn n | FileOut n -> pr "String %s" n + | BufferIn n -> + pr "byte[] %s" n | StringList n | DeviceList n -> pr "String[] %s" n | Bool n -> @@ -9884,6 +10140,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | FileIn n | FileOut n -> pr ", jstring j%s" n + | BufferIn n -> + pr ", jbyteArray j%s" n | StringList n | DeviceList n -> pr ", jobjectArray j%s" n | Bool n -> @@ -9939,6 +10197,9 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | FileIn n | FileOut n -> pr " const char *%s;\n" n + | BufferIn n -> + pr " jbyte *%s;\n" n; + pr " size_t %s_size;\n" n | StringList n | DeviceList n -> pr " int %s_len;\n" n; pr " const char **%s;\n" n @@ -9978,6 +10239,9 @@ Java_com_redhat_et_libguestfs_GuestFS__1close * a NULL parameter. *) pr " %s = j%s ? (*env)->GetStringUTFChars (env, j%s, NULL) : NULL;\n" n n n + | BufferIn n -> + pr " %s = (*env)->GetByteArrayElements (env, j%s, NULL);\n" n n; + pr " %s_size = (*env)->GetArrayLength (env, j%s);\n" n n | StringList n | DeviceList n -> pr " %s_len = (*env)->GetArrayLength (env, j%s);\n" n n; pr " %s = guestfs_safe_malloc (g, sizeof (char *) * (%s_len+1));\n" n n; @@ -10010,6 +10274,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | OptString n -> pr " if (j%s)\n" n; pr " (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n + | BufferIn n -> + pr " (*env)->ReleaseByteArrayElements (env, j%s, %s, 0);\n" n n | StringList n | DeviceList n -> pr " for (i = 0; i < %s_len; ++i) {\n" n; pr " jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n" @@ -10284,7 +10550,10 @@ last_error h = do function | FileIn n | FileOut n - | Pathname n | Device n | Dev_or_Path n | String n -> pr "withCString %s $ \\%s -> " n n + | Pathname n | Device n | Dev_or_Path n | String n -> + pr "withCString %s $ \\%s -> " n n + | BufferIn n -> + pr "withCStringLen %s $ \\(%s, %s_size) -> " n n n | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n | StringList n | DeviceList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n | Bool _ | Int _ | Int64 _ -> () @@ -10298,6 +10567,7 @@ last_error h = do | 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 + | BufferIn n -> sprintf "%s (fromIntegral %s_size)" n n ) (snd style) in pr "withForeignPtr h (\\p -> c_%s %s)\n" name (String.concat " " ("p" :: args)); @@ -10348,6 +10618,9 @@ and generate_haskell_prototype ~handle ?(hs = false) style = fun arg -> (match arg with | Pathname _ | Device _ | Dev_or_Path _ | String _ -> pr "%s" string + | BufferIn _ -> + if hs then pr "String" + else pr "CString -> CInt" | OptString _ -> if hs then pr "Maybe String" else pr "CString" | StringList _ | DeviceList _ -> if hs then pr "[String]" else pr "Ptr CString" | Bool _ -> pr "%s" bool @@ -10537,7 +10810,8 @@ namespace Guestfs List.iter ( function | Pathname n | Device n | Dev_or_Path n | String n | OptString n - | FileIn n | FileOut n -> + | FileIn n | FileOut n + | BufferIn n -> pr ", [In] string %s" n | StringList n | DeviceList n -> pr ", [In] string[] %s" n @@ -10560,7 +10834,8 @@ namespace Guestfs List.iter ( function | Pathname n | Device n | Dev_or_Path n | String n | OptString n - | FileIn n | FileOut n -> + | FileIn n | FileOut n + | BufferIn n -> next (); pr "string %s" n | StringList n | DeviceList n -> next (); pr "string[] %s" n @@ -10665,6 +10940,10 @@ print_strings (char *const *argv) | String n | FileIn n | FileOut n -> pr " printf (\"%%s\\n\", %s);\n" n + | BufferIn n -> + pr " for (size_t i = 0; i < %s_size; ++i)\n" n; + pr " printf (\"<%%02x>\", %s[i]);\n" n; + pr " printf (\"\\n\");\n" | OptString n -> pr " printf (\"%%s\\n\", %s ? %s : \"null\");\n" n n | StringList n | DeviceList n -> pr " print_strings (%s);\n" n | Bool n -> pr " printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n @@ -10788,6 +11067,7 @@ let () = | CallInt64 i when i >= 0L -> Int64.to_string i ^ "L" | CallInt64 i (* when i < 0L *) -> "(" ^ Int64.to_string i ^ "L)" | CallBool b -> string_of_bool b + | CallBuffer s -> sprintf "%S" s ) args ) in @@ -10822,6 +11102,7 @@ my $g = Sys::Guestfs->new (); | CallInt i -> string_of_int i | CallInt64 i -> Int64.to_string i | CallBool b -> if b then "1" else "0" + | CallBuffer s -> "\"" ^ c_quote s ^ "\"" ) args ) in @@ -10853,6 +11134,7 @@ g = guestfs.GuestFS () | CallInt i -> string_of_int i | CallInt64 i -> Int64.to_string i | CallBool b -> if b then "1" else "0" + | CallBuffer s -> "\"" ^ c_quote s ^ "\"" ) args ) in @@ -10884,6 +11166,7 @@ g = Guestfs::create() | CallInt i -> string_of_int i | CallInt64 i -> Int64.to_string i | CallBool b -> string_of_bool b + | CallBuffer s -> "\"" ^ c_quote s ^ "\"" ) args ) in @@ -10920,6 +11203,10 @@ public class Bindtests { | CallInt i -> string_of_int i | CallInt64 i -> Int64.to_string i | CallBool b -> string_of_bool b + | CallBuffer s -> + "new byte[] { " ^ String.concat "," ( + map_chars (fun c -> string_of_int (Char.code c)) s + ) ^ " }" ) args ) in @@ -10965,6 +11252,7 @@ main = do | CallInt64 i -> Int64.to_string i | CallBool true -> "True" | CallBool false -> "False" + | CallBuffer s -> "\"" ^ c_quote s ^ "\"" ) args ) in @@ -10981,43 +11269,56 @@ main = do and generate_lang_bindtests call = call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList []; CallBool false; - CallInt 0; CallInt64 0L; CallString "123"; CallString "456"]; + CallInt 0; CallInt64 0L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString None; CallStringList []; CallBool false; - CallInt 0; CallInt64 0L; CallString "123"; CallString "456"]; + CallInt 0; CallInt64 0L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString ""; CallOptString (Some "def"); CallStringList []; CallBool false; - CallInt 0; CallInt64 0L; CallString "123"; CallString "456"]; + CallInt 0; CallInt64 0L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString ""; CallOptString (Some ""); CallStringList []; CallBool false; - CallInt 0; CallInt64 0L; CallString "123"; CallString "456"]; + CallInt 0; CallInt64 0L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool false; - CallInt 0; CallInt64 0L; CallString "123"; CallString "456"]; + CallInt 0; CallInt64 0L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"; "2"]; CallBool false; - CallInt 0; CallInt64 0L; CallString "123"; CallString "456"]; + CallInt 0; CallInt64 0L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool true; - CallInt 0; CallInt64 0L; CallString "123"; CallString "456"]; + CallInt 0; CallInt64 0L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool false; - CallInt (-1); CallInt64 (-1L); CallString "123"; CallString "456"]; + CallInt (-1); CallInt64 (-1L); CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool false; - CallInt (-2); CallInt64 (-2L); CallString "123"; CallString "456"]; + CallInt (-2); CallInt64 (-2L); CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool false; - CallInt 1; CallInt64 1L; CallString "123"; CallString "456"]; + CallInt 1; CallInt64 1L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool false; - CallInt 2; CallInt64 2L; CallString "123"; CallString "456"]; + CallInt 2; CallInt64 2L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool false; - CallInt 4095; CallInt64 4095L; CallString "123"; CallString "456"]; + CallInt 4095; CallInt64 4095L; CallString "123"; CallString "456"; + CallBuffer "abc\000abc"]; call "test0" [CallString "abc"; CallOptString (Some "def"); CallStringList ["1"]; CallBool false; - CallInt 0; CallInt64 0L; CallString ""; CallString ""] + CallInt 0; CallInt64 0L; CallString ""; CallString ""; + CallBuffer "abc\000abc"] (* XXX Add here tests of the return and error functions. *) @@ -11509,17 +11810,7 @@ let inspect ?connect ?xml names = parse_operatingsystems xml " -(* This is used to generate the src/MAX_PROC_NR file which - * contains the maximum procedure number, a surrogate for the - * ABI version number. See src/Makefile.am for the details. - *) and generate_max_proc_nr () = - let proc_nrs = List.map ( - fun (_, _, proc_nr, _, _, _, _) -> proc_nr - ) daemon_functions in - - let max_proc_nr = List.fold_left max 0 proc_nrs in - pr "%d\n" max_proc_nr let output_to filename k =