X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fgenerator.ml;h=00f3c4ecfdc3500ba7b24dc61c46b8d5ed932361;hp=e3293d1de44277fd051b57926bc9a8669e91e64e;hb=7c4f90ddb8378dcf90cb0cc219105dccaa8a9d6e;hpb=7d41d75c1d4e6dbe61f6cd353bc104663340483f diff --git a/src/generator.ml b/src/generator.ml index e3293d1..00f3c4e 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -25,9 +25,8 @@ * daemon/.c to write the implementation. * * After editing this file, run it (./src/generator.ml) to regenerate all the - * output files. Note that if you are using a separate build directory you must - * run generator.ml from your top level build directory. You must also have run - * configure before generator.ml will run. + * output files. Note that if you are using a separate build directory you + * must run generator.ml from the _source_ directory. * * IMPORTANT: This script should NOT print any warnings. If it prints * warnings, you should treat them as errors. @@ -45,39 +44,62 @@ and ret = * indication, ie. 0 or -1. *) | RErr + (* "RInt" as a return value means an int which is -1 for error * or any value >= 0 on success. Only use this for smallish * positive ints (0 <= i < 2^30). *) | RInt of string + (* "RInt64" is the same as RInt, but is guaranteed to be able * to return a full 64 bit value, _except_ that -1 means error * (so -1 cannot be a valid, non-error return value). *) | RInt64 of string + (* "RBool" is a bool return value which can be true/false or * -1 for error. *) | RBool of string + (* "RConstString" is a string that refers to a constant value. + * The return value must NOT be NULL (since NULL indicates + * an error). + * * Try to avoid using this. In particular you cannot use this * for values returned from the daemon, because there is no * thread-safe way to return them in the C API. *) | RConstString of string - (* "RString" and "RStringList" are caller-frees. *) + + (* "RConstOptString" is an even more broken version of + * "RConstString". The returned string may be NULL and there + * is no way to return an error indication. Avoid using this! + *) + | RConstOptString of string + + (* "RString" is a returned string. It must NOT be NULL, since + * a NULL return indicates an error. The caller frees this. + *) | RString of string + + (* "RStringList" is a list of strings. No string in the list + * can be NULL. The caller frees the strings and the array. + *) | RStringList of string + (* "RStruct" is a function which returns a single named structure * or an error indication (in C, a struct, and in other languages * with varying representations, but usually very efficient). See * after the function list below for the structures. *) | RStruct of string * string (* name of retval, name of struct *) + (* "RStructList" is a function which returns either a list/array * of structures (could be zero-length), or an error indication. *) | RStructList of string * string (* name of retval, name of struct *) + (* Key-value pairs of untyped strings. Turns into a hashtable or * dictionary in languages which support it. DON'T use this as a * general "bucket" for results. Prefer a stronger typed return @@ -86,16 +108,21 @@ and ret = * inefficient. Keys should be unique. NULLs are not permitted. *) | RHashtable of string -(* Not implemented: + (* "RBufferOut" is handled almost exactly like RString, but * it allows the string to contain arbitrary 8 bit data including * ASCII NUL. In the C API this causes an implicit extra parameter - * to be added of type . Other programming languages - * support strings with arbitrary 8 bit data. At the RPC layer - * we have to use the opaque<> type instead of string<>. + * to be added of type . The extra parameter + * returns the actual size of the return buffer in bytes. + * + * Other programming languages support strings with arbitrary 8 bit + * data. + * + * At the RPC layer we have to use the opaque<> type instead of + * string<>. Returned data is still limited to the max message + * size (ie. ~ 2 MB). *) | RBufferOut of string -*) and args = argt list (* Function parameters, guestfs handle is implicit. *) @@ -142,15 +169,7 @@ type flags = | FishAction of string (* call this function in guestfish *) | NotInFish (* do not export via guestfish *) | NotInDocs (* do not add this function to documentation *) - -let protocol_limit_warning = - "Because of the message protocol, there is a transfer limit -of somewhere between 2MB and 4MB. To transfer large files you should use -FTP." - -let danger_will_robinson = - "B." + | DeprecatedBy of string (* function is deprecated, use .. instead *) (* You can supply zero or as many tests as you want per API call. * @@ -224,6 +243,10 @@ and test = *) | TestOutputLength of seq * int (* Run the command sequence and expect the output of the final + * command to be a buffer (RBufferOut), ie. string + size. + *) + | TestOutputBuffer of seq * string + (* Run the command sequence and expect the output of the final * command to be a structure. *) | TestOutputStruct of seq * test_field_compare list @@ -261,14 +284,17 @@ and test_init = * a bad idea. *) | InitNone + (* Block devices are empty and no filesystems are mounted. *) | InitEmpty + (* /dev/sda contains a single partition /dev/sda1, which is formatted * as ext2, empty [except for lost+found] and mounted on /. * /dev/sdb and /dev/sdc may have random content. * No LVM. *) | InitBasicFS + (* /dev/sda: * /dev/sda1 (is a PV): * /dev/VG/LV (size 8MB): @@ -277,6 +303,11 @@ and test_init = *) | InitBasicFSonLVM + (* /dev/sdd (the squashfs, see images/ directory in source) + * is mounted on / + *) + | InitSquashFS + (* Sequence of commands for testing. *) and seq = cmd list and cmd = string list @@ -307,6 +338,7 @@ let test_all_rets = [ "test0rint64", RInt64 "valout"; "test0rbool", RBool "valout"; "test0rconststring", RConstString "valout"; + "test0rconstoptstring", RConstOptString "valout"; "test0rstring", RString "valout"; "test0rstringlist", RStringList "valout"; "test0rstruct", RStruct ("valout", "lvm_pv"); @@ -501,7 +533,7 @@ Return the current search path. This is always non-NULL. If it wasn't set already, then this will return the default path."); - ("set_append", (RErr, [String "append"]), -1, [FishAlias "append"], + ("set_append", (RErr, [OptString "append"]), -1, [FishAlias "append"], [], "add options to kernel command line", "\ @@ -514,7 +546,7 @@ C environment variable. Setting C to C means I additional options are passed (libguestfs always adds a few of its own)."); - ("get_append", (RConstString "append", []), -1, [], + ("get_append", (RConstOptString "append", []), -1, [], (* This cannot be tested with the current framework. The * function can return NULL in normal operations, which the * test framework interprets as an error. @@ -764,17 +796,16 @@ update the timestamps on a file, or, if the file does not exist, to create a new zero-length file."); ("cat", (RString "content", [String "path"]), 4, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "new file contents"; "0"]; - ["cat"; "/new"]], "new file contents")], + [InitSquashFS, Always, TestOutput ( + [["cat"; "/known-2"]], "abcdef\n")], "list the contents of a file", "\ Return the contents of the file named C. Note that this function cannot correctly handle binary files (specifically, files containing C<\\0> character which is treated -as end of string). For those you need to use the C -function which has a more complex interface."); +as end of string). For those you need to use the C +or C functions which have a more complex interface."); ("ll", (RString "listing", [String "directory"]), 5, [], [], (* XXX Tricky to test because it depends on the exact format @@ -913,12 +944,10 @@ List all the logical volumes detected. This is the equivalent of the L command. The \"full\" version includes all fields."); ("read_lines", (RStringList "lines", [String "path"]), 15, [], - [InitBasicFS, Always, TestOutputList ( - [["write_file"; "/new"; "line1\r\nline2\nline3"; "0"]; - ["read_lines"; "/new"]], ["line1"; "line2"; "line3"]); - InitBasicFS, Always, TestOutputList ( - [["write_file"; "/new"; ""; "0"]; - ["read_lines"; "/new"]], [])], + [InitSquashFS, Always, TestOutputList ( + [["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]); + InitSquashFS, Always, TestOutputList ( + [["read_lines"; "/empty"]], [])], "read file as lines", "\ Return the contents of the file named C. @@ -1179,12 +1208,10 @@ names, you will need to locate and parse the password file yourself (Augeas support makes this relatively easy)."); ("exists", (RBool "existsflag", [String "path"]), 36, [], - [InitBasicFS, Always, TestOutputTrue ( - [["touch"; "/new"]; - ["exists"; "/new"]]); - InitBasicFS, Always, TestOutputTrue ( - [["mkdir"; "/new"]; - ["exists"; "/new"]])], + [InitSquashFS, Always, TestOutputTrue ( + [["exists"; "/empty"]]); + InitSquashFS, Always, TestOutputTrue ( + [["exists"; "/directory"]])], "test if file or directory exists", "\ This returns C if and only if there is a file, directory @@ -1193,12 +1220,10 @@ This returns C if and only if there is a file, directory See also C, C, C."); ("is_file", (RBool "fileflag", [String "path"]), 37, [], - [InitBasicFS, Always, TestOutputTrue ( - [["touch"; "/new"]; - ["is_file"; "/new"]]); - InitBasicFS, Always, TestOutputFalse ( - [["mkdir"; "/new"]; - ["is_file"; "/new"]])], + [InitSquashFS, Always, TestOutputTrue ( + [["is_file"; "/known-1"]]); + InitSquashFS, Always, TestOutputFalse ( + [["is_file"; "/directory"]])], "test if file exists", "\ This returns C if and only if there is a file @@ -1208,12 +1233,10 @@ other objects like directories. See also C."); ("is_dir", (RBool "dirflag", [String "path"]), 38, [], - [InitBasicFS, Always, TestOutputFalse ( - [["touch"; "/new"]; - ["is_dir"; "/new"]]); - InitBasicFS, Always, TestOutputTrue ( - [["mkdir"; "/new"]; - ["is_dir"; "/new"]])], + [InitSquashFS, Always, TestOutputFalse ( + [["is_dir"; "/known-3"]]); + InitSquashFS, Always, TestOutputTrue ( + [["is_dir"; "/directory"]])], "test if file exists", "\ This returns C if and only if there is a directory @@ -1371,7 +1394,9 @@ contains the filesystem."); This returns the list of currently mounted filesystems. It returns the list of devices (eg. C, C). -Some internal mounts are not shown."); +Some internal mounts are not shown. + +See also: C"); ("umount_all", (RErr, []), 47, [FishAlias "unmount-all"], [InitBasicFS, Always, TestOutputList ( @@ -1405,21 +1430,22 @@ This command removes all LVM logical volumes, volume groups and physical volumes."); ("file", (RString "description", [String "path"]), 49, [], - [InitBasicFS, Always, TestOutput ( - [["touch"; "/new"]; - ["file"; "/new"]], "empty"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "some content\n"; "0"]; - ["file"; "/new"]], "ASCII text"); - InitBasicFS, Always, TestLastFail ( - [["file"; "/nofile"]])], + [InitSquashFS, Always, TestOutput ( + [["file"; "/empty"]], "empty"); + InitSquashFS, Always, TestOutput ( + [["file"; "/known-1"]], "ASCII text"); + InitSquashFS, Always, TestLastFail ( + [["file"; "/notexists"]])], "determine file type", "\ This call uses the standard L command to determine the type or contents of the file. This also works on devices, for example to find out whether a partition contains a filesystem. -The exact command which runs is C. Note in +This call will also transparently look inside various types +of compressed file. + +The exact command which runs is C. Note in particular that the filename is not prepended to the output (the C<-b> option)."); @@ -1557,9 +1583,8 @@ result into a list of lines. See also: C"); ("stat", (RStruct ("statbuf", "stat"), [String "path"]), 52, [], - [InitBasicFS, Always, TestOutputStruct ( - [["touch"; "/new"]; - ["stat"; "/new"]], [CompareWithInt ("size", 0)])], + [InitSquashFS, Always, TestOutputStruct ( + [["stat"; "/empty"]], [CompareWithInt ("size", 0)])], "get file information", "\ Returns file information for the given C. @@ -1567,9 +1592,8 @@ Returns file information for the given C. This is the same as the C system call."); ("lstat", (RStruct ("statbuf", "stat"), [String "path"]), 53, [], - [InitBasicFS, Always, TestOutputStruct ( - [["touch"; "/new"]; - ["lstat"; "/new"]], [CompareWithInt ("size", 0)])], + [InitSquashFS, Always, TestOutputStruct ( + [["lstat"; "/empty"]], [CompareWithInt ("size", 0)])], "get file information for a symbolic link", "\ Returns file information for the given C. @@ -1581,9 +1605,9 @@ refers to. This is the same as the C system call."); ("statvfs", (RStruct ("statbuf", "statvfs"), [String "path"]), 54, [], - [InitBasicFS, Always, TestOutputStruct ( - [["statvfs"; "/"]], [CompareWithInt ("namemax", 255); - CompareWithInt ("bsize", 1024)])], + [InitSquashFS, Always, TestOutputStruct ( + [["statvfs"; "/"]], [CompareWithInt ("namemax", 256); + CompareWithInt ("bsize", 131072)])], "get file system statistics", "\ Returns file system statistics for any mounted file system. @@ -1746,35 +1770,22 @@ C can also be a named pipe. See also C, C."); ("checksum", (RString "checksum", [String "csumtype"; String "path"]), 68, [], - [InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "test\n"; "0"]; - ["checksum"; "crc"; "/new"]], "935282863"); - InitBasicFS, Always, TestLastFail ( - [["checksum"; "crc"; "/new"]]); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "test\n"; "0"]; - ["checksum"; "md5"; "/new"]], "d8e8fca2dc0f896fd7cb4cb0031ba249"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "test\n"; "0"]; - ["checksum"; "sha1"; "/new"]], "4e1243bd22c66e76c2ba9eddc1f91394e57f9f83"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "test\n"; "0"]; - ["checksum"; "sha224"; "/new"]], "52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "test\n"; "0"]; - ["checksum"; "sha256"; "/new"]], "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "test\n"; "0"]; - ["checksum"; "sha384"; "/new"]], "109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "test\n"; "0"]; - ["checksum"; "sha512"; "/new"]], "0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123"); - InitBasicFS, Always, TestOutput ( - (* RHEL 5 thinks this is an HFS+ filesystem unless we give - * the type explicitly. - *) - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["checksum"; "md5"; "/known-3"]], "46d6ca27ee07cdc6fa99c2e138cc522c")], + [InitSquashFS, Always, TestOutput ( + [["checksum"; "crc"; "/known-3"]], "2891671662"); + InitSquashFS, Always, TestLastFail ( + [["checksum"; "crc"; "/notexists"]]); + InitSquashFS, Always, TestOutput ( + [["checksum"; "md5"; "/known-3"]], "46d6ca27ee07cdc6fa99c2e138cc522c"); + InitSquashFS, Always, TestOutput ( + [["checksum"; "sha1"; "/known-3"]], "b7ebccc3ee418311091c3eda0a45b83c0a770f15"); + InitSquashFS, Always, TestOutput ( + [["checksum"; "sha224"; "/known-3"]], "d2cd1774b28f3659c14116be0a6dc2bb5c4b350ce9cd5defac707741"); + InitSquashFS, Always, TestOutput ( + [["checksum"; "sha256"; "/known-3"]], "75bb71b90cd20cb13f86d2bea8dad63ac7194e7517c3b52b8d06ff52d3487d30"); + InitSquashFS, Always, TestOutput ( + [["checksum"; "sha384"; "/known-3"]], "5fa7883430f357b5d7b7271d3a1d2872b51d73cba72731de6863d3dea55f30646af2799bef44d5ea776a5ec7941ac640"); + InitSquashFS, Always, TestOutput ( + [["checksum"; "sha512"; "/known-3"]], "2794062c328c6b216dca90443b7f7134c5f40e56bd0ed7853123275a09982a6f992e6ca682f9d2fba34a4c5e870d8fe077694ff831e3032a004ee077e00603f6")], "compute MD5, SHAx or CRC checksum of file", "\ This call computes the MD5, SHAx or CRC checksum of the @@ -2212,21 +2223,18 @@ true if their content is exactly equal, or false otherwise. The external L program is used for the comparison."); ("strings", (RStringList "stringsout", [String "path"]), 94, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutputList ( - [["write_file"; "/new"; "hello\nworld\n"; "0"]; - ["strings"; "/new"]], ["hello"; "world"]); - InitBasicFS, Always, TestOutputList ( - [["touch"; "/new"]; - ["strings"; "/new"]], [])], + [InitSquashFS, Always, TestOutputList ( + [["strings"; "/known-5"]], ["abcdefghi"; "jklmnopqr"]); + InitSquashFS, Always, TestOutputList ( + [["strings"; "/empty"]], [])], "print the printable strings in a file", "\ This runs the L command on a file and returns the list of printable strings found."); ("strings_e", (RStringList "stringsout", [String "encoding"; String "path"]), 95, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutputList ( - [["write_file"; "/new"; "hello\nworld\n"; "0"]; - ["strings_e"; "b"; "/new"]], []); + [InitSquashFS, Always, TestOutputList ( + [["strings_e"; "b"; "/known-5"]], []); InitBasicFS, Disabled, TestOutputList ( [["write_file"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"; "24"]; ["strings_e"; "b"; "/new"]], ["hello"; "world"])], @@ -2243,15 +2251,13 @@ show strings inside Windows/x86 files. The returned strings are transcoded to UTF-8."); ("hexdump", (RString "dump", [String "path"]), 96, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "hello\nworld\n"; "12"]; - ["hexdump"; "/new"]], "00000000 68 65 6c 6c 6f 0a 77 6f 72 6c 64 0a |hello.world.|\n0000000c\n"); + [InitSquashFS, Always, TestOutput ( + [["hexdump"; "/known-4"]], "00000000 61 62 63 0a 64 65 66 0a 67 68 69 |abc.def.ghi|\n0000000b\n"); (* Test for RHBZ#501888c2 regression which caused large hexdump * commands to segfault. *) - InitBasicFS, Always, TestRun ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["hexdump"; "/100krandom"]])], + InitSquashFS, Always, TestRun ( + [["hexdump"; "/100krandom"]])], "dump a file in hexadecimal", "\ This runs C on the given C. The result is @@ -2588,51 +2594,44 @@ directory and its contents after use. See also: L"); ("wc_l", (RInt "lines", [String "path"]), 118, [], - [InitBasicFS, Always, TestOutputInt ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["wc_l"; "/10klines"]], 10000)], + [InitSquashFS, Always, TestOutputInt ( + [["wc_l"; "/10klines"]], 10000)], "count lines in a file", "\ This command counts the lines in a file, using the C external command."); ("wc_w", (RInt "words", [String "path"]), 119, [], - [InitBasicFS, Always, TestOutputInt ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["wc_w"; "/10klines"]], 10000)], + [InitSquashFS, Always, TestOutputInt ( + [["wc_w"; "/10klines"]], 10000)], "count words in a file", "\ This command counts the words in a file, using the C external command."); ("wc_c", (RInt "chars", [String "path"]), 120, [], - [InitBasicFS, Always, TestOutputInt ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["wc_c"; "/100kallspaces"]], 102400)], + [InitSquashFS, Always, TestOutputInt ( + [["wc_c"; "/100kallspaces"]], 102400)], "count characters in a file", "\ This command counts the characters in a file, using the C external command."); ("head", (RStringList "lines", [String "path"]), 121, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["head"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz";"3abcdefghijklmnopqrstuvwxyz";"4abcdefghijklmnopqrstuvwxyz";"5abcdefghijklmnopqrstuvwxyz";"6abcdefghijklmnopqrstuvwxyz";"7abcdefghijklmnopqrstuvwxyz";"8abcdefghijklmnopqrstuvwxyz";"9abcdefghijklmnopqrstuvwxyz"])], + [InitSquashFS, Always, TestOutputList ( + [["head"; "/10klines"]], ["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 a list of strings."); ("head_n", (RStringList "lines", [Int "nrlines"; String "path"]), 122, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["head_n"; "3"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]); - InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["head_n"; "-9997"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]); - InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["head_n"; "0"; "/10klines"]], [])], + [InitSquashFS, Always, TestOutputList ( + [["head_n"; "3"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]); + InitSquashFS, Always, TestOutputList ( + [["head_n"; "-9997"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]); + InitSquashFS, Always, TestOutputList ( + [["head_n"; "0"; "/10klines"]], [])], "return first N lines of a file", "\ If the parameter C is a positive number, this returns the first @@ -2644,24 +2643,20 @@ from the file C, excluding the last C lines. If the parameter C is zero, this returns an empty list."); ("tail", (RStringList "lines", [String "path"]), 123, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["tail"; "/10klines"]], ["9990abcdefghijklmnopqrstuvwxyz";"9991abcdefghijklmnopqrstuvwxyz";"9992abcdefghijklmnopqrstuvwxyz";"9993abcdefghijklmnopqrstuvwxyz";"9994abcdefghijklmnopqrstuvwxyz";"9995abcdefghijklmnopqrstuvwxyz";"9996abcdefghijklmnopqrstuvwxyz";"9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"])], + [InitSquashFS, Always, TestOutputList ( + [["tail"; "/10klines"]], ["9990abcdefghijklmnopqrstuvwxyz";"9991abcdefghijklmnopqrstuvwxyz";"9992abcdefghijklmnopqrstuvwxyz";"9993abcdefghijklmnopqrstuvwxyz";"9994abcdefghijklmnopqrstuvwxyz";"9995abcdefghijklmnopqrstuvwxyz";"9996abcdefghijklmnopqrstuvwxyz";"9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"])], "return last 10 lines of a file", "\ This command returns up to the last 10 lines of a file as a list of strings."); ("tail_n", (RStringList "lines", [Int "nrlines"; String "path"]), 124, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["tail_n"; "3"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]); - InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["tail_n"; "-9998"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]); - InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["tail_n"; "0"; "/10klines"]], [])], + [InitSquashFS, Always, TestOutputList ( + [["tail_n"; "3"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]); + InitSquashFS, Always, TestOutputList ( + [["tail_n"; "-9998"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]); + InitSquashFS, Always, TestOutputList ( + [["tail_n"; "0"; "/10klines"]], [])], "return last N lines of a file", "\ If the parameter C is a positive number, this returns the last @@ -2698,9 +2693,8 @@ is I intended that you try to parse the output string. Use C from programs."); ("du", (RInt64 "sizekb", [String "path"]), 127, [], - [InitBasicFS, Always, TestOutputInt ( - [["mkdir"; "/p"]; - ["du"; "/p"]], 1 (* ie. 1 block, so depends on ext3 blocksize *))], + [InitSquashFS, Always, TestOutputInt ( + [["du"; "/directory"]], 0 (* squashfs doesn't have blocks *))], "estimate file space usage", "\ This command runs the C command to estimate file space @@ -2714,9 +2708,8 @@ The result is the estimated size in I (ie. units of 1024 bytes)."); ("initrd_list", (RStringList "filenames", [String "path"]), 128, [], - [InitBasicFS, Always, TestOutputList ( - [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]; - ["initrd_list"; "/initrd"]], ["empty";"known-1";"known-2";"known-3"])], + [InitSquashFS, Always, TestOutputList ( + [["initrd_list"; "/initrd"]], ["empty";"known-1";"known-2";"known-3";"known-4"; "known-5"])], "list files in an initrd", "\ This command lists out files contained in an initrd. @@ -2841,6 +2834,50 @@ All entries in the directory are returned, including C<.> and C<..>. The entries are I sorted, but returned in the same order as the underlying filesystem. +Also this call returns basic file type information about each +file. The C field will contain one of the following characters: + +=over 4 + +=item 'b' + +Block special + +=item 'c' + +Char special + +=item 'd' + +Directory + +=item 'f' + +FIFO (named pipe) + +=item 'l' + +Symbolic link + +=item 'r' + +Regular file + +=item 's' + +Socket + +=item 'u' + +Unknown file type + +=item '?' + +The L returned a C field with an +unexpected value + +=back + This function is primarily intended for use by programs. To get a simple list of names, use C. To get a printable directory for human consumption, use C."); @@ -2857,7 +2894,7 @@ were rarely if ever used anyway. See also C and the L manpage."); - ("zfile", (RString "description", [String "method"; String "path"]), 140, [], + ("zfile", (RString "description", [String "method"; String "path"]), 140, [DeprecatedBy "file"], [], "determine file type inside a compressed file", "\ @@ -2866,7 +2903,222 @@ using C. C must be one of C, C or C. -See also: C"); +Since 1.0.63, use C instead which can now +process compressed files."); + + ("getxattrs", (RStructList ("xattrs", "xattr"), [String "path"]), 141, [], + [], + "list extended attributes of a file or directory", + "\ +This call lists the extended attributes of the file or directory +C. + +At the system call level, this is a combination of the +L and L calls. + +See also: C, L."); + + ("lgetxattrs", (RStructList ("xattrs", "xattr"), [String "path"]), 142, [], + [], + "list extended attributes of a file or directory", + "\ +This is the same as C, but if C +is a symbolic link, then it returns the extended attributes +of the link itself."); + + ("setxattr", (RErr, [String "xattr"; + String "val"; Int "vallen"; (* will be BufferIn *) + String "path"]), 143, [], + [], + "set extended attribute of a file or directory", + "\ +This call sets the extended attribute named C +of the file C to the value C (of length C). +The value is arbitrary 8 bit data. + +See also: C, L."); + + ("lsetxattr", (RErr, [String "xattr"; + String "val"; Int "vallen"; (* will be BufferIn *) + String "path"]), 144, [], + [], + "set extended attribute of a file or directory", + "\ +This is the same as C, but if C +is a symbolic link, then it sets an extended attribute +of the link itself."); + + ("removexattr", (RErr, [String "xattr"; String "path"]), 145, [], + [], + "remove extended attribute of a file or directory", + "\ +This call removes the extended attribute named C +of the file C. + +See also: C, L."); + + ("lremovexattr", (RErr, [String "xattr"; String "path"]), 146, [], + [], + "remove extended attribute of a file or directory", + "\ +This is the same as C, but if C +is a symbolic link, then it removes an extended attribute +of the link itself."); + + ("mountpoints", (RHashtable "mps", []), 147, [], + [], + "show mountpoints", + "\ +This call is similar to C. That call returns +a list of devices. This one returns a hash table (map) of +device name to directory where the device is mounted."); + + ("mkmountpoint", (RErr, [String "path"]), 148, [], + [], + "create a mountpoint", + "\ +C and C are +specialized calls that can be used to create extra mountpoints +before mounting the first filesystem. + +These calls are I necessary in some very limited circumstances, +mainly the case where you want to mount a mix of unrelated and/or +read-only filesystems together. + +For example, live CDs often contain a \"Russian doll\" nest of +filesystems, an ISO outer layer, with a squashfs image inside, with +an ext2/3 image inside that. You can unpack this as follows +in guestfish: + + add-ro Fedora-11-i686-Live.iso + run + mkmountpoint /cd + mkmountpoint /squash + mkmountpoint /ext3 + mount /dev/sda /cd + mount-loop /cd/LiveOS/squashfs.img /squash + mount-loop /squash/LiveOS/ext3fs.img /ext3 + +The inner filesystem is now unpacked under the /ext3 mountpoint."); + + ("rmmountpoint", (RErr, [String "path"]), 149, [], + [], + "remove a mountpoint", + "\ +This calls removes a mountpoint that was previously created +with C. See C +for full details."); + + ("read_file", (RBufferOut "content", [String "path"]), 150, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputBuffer ( + [["read_file"; "/known-4"]], "abc\ndef\nghi")], + "read a file", + "\ +This calls returns the contents of the file C as a +buffer. + +Unlike C, this function can correctly +handle files that contain embedded ASCII NUL characters. +However unlike C, this function is limited +in the total size of file that can be handled."); + + ("grep", (RStringList "lines", [String "regex"; String "path"]), 151, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["grep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"]); + InitSquashFS, Always, TestOutputList ( + [["grep"; "nomatch"; "/test-grep.txt"]], [])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("egrep", (RStringList "lines", [String "regex"; String "path"]), 152, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["egrep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("fgrep", (RStringList "lines", [String "pattern"; String "path"]), 153, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["fgrep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("grepi", (RStringList "lines", [String "regex"; String "path"]), 154, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["grepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("egrepi", (RStringList "lines", [String "regex"; String "path"]), 155, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["egrepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("fgrepi", (RStringList "lines", [String "pattern"; String "path"]), 156, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["fgrepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("zgrep", (RStringList "lines", [String "regex"; String "path"]), 157, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["zgrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("zegrep", (RStringList "lines", [String "regex"; String "path"]), 158, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["zegrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("zfgrep", (RStringList "lines", [String "pattern"; String "path"]), 159, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["zfgrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("zgrepi", (RStringList "lines", [String "regex"; String "path"]), 160, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["zgrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("zegrepi", (RStringList "lines", [String "regex"; String "path"]), 161, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["zegrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); + + ("zfgrepi", (RStringList "lines", [String "pattern"; String "path"]), 162, [ProtocolLimitWarning], + [InitSquashFS, Always, TestOutputList ( + [["zfgrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])], + "return lines matching a pattern", + "\ +This calls the external C program and returns the +matching lines."); ] @@ -2883,6 +3135,7 @@ let all_functions_sorted = type field = | FChar (* C 'char' (really, a 7 bit byte). *) | FString (* nul-terminated ASCII string. *) + | FBuffer (* opaque buffer of bytes, (char *, int) pair *) | FUInt32 | FInt32 | FUInt64 @@ -3021,6 +3274,12 @@ let structs = [ "release", FInt64; "extra", FString; ]; + + (* Extended attribute. *) + "xattr", [ + "attrname", FString; + "attrval", FBuffer; + ]; ] (* end of structs *) (* Ugh, Java has to be different .. @@ -3035,6 +3294,7 @@ let java_structs = [ "statvfs", "StatVFS"; "dirent", "Dirent"; "version", "Version"; + "xattr", "XAttr"; ] (* Used for testing language bindings. *) @@ -3187,9 +3447,35 @@ let seq_of_test = function | TestOutputListOfDevices (s, _) | TestOutputInt (s, _) | TestOutputIntOp (s, _, _) | TestOutputTrue s | TestOutputFalse s - | TestOutputLength (s, _) | TestOutputStruct (s, _) + | TestOutputLength (s, _) | TestOutputBuffer (s, _) + | TestOutputStruct (s, _) | TestLastFail s -> s +(* Handling for function flags. *) +let protocol_limit_warning = + "Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP." + +let danger_will_robinson = + "B." + +let deprecation_notice flags = + try + let alt = + find_map (function DeprecatedBy str -> Some str | _ -> None) flags in + let txt = + sprintf "This function is deprecated. +In new code, use the C<%s> call instead. + +Deprecated functions will not be removed from the API, but the +fact that they are deprecated indicates that there are problems +with correct use of these functions." alt in + Some txt + with + Not_found -> None + (* Check function names etc. for consistency. *) let check_functions () = let contains_uppercase str = @@ -3241,9 +3527,10 @@ let check_functions () = (match fst style with | RErr -> () - | RInt n | RInt64 n | RBool n | RConstString n | RString n + | RInt n | RInt64 n | RBool n + | RConstString n | RConstOptString n | RString n | RStringList n | RStruct (n, _) | RStructList (n, _) - | RHashtable n -> + | RHashtable n | RBufferOut n -> check_arg_ret_name n ); List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) (snd style) @@ -3403,6 +3690,10 @@ let rec generate_actions_pod () = | RConstString _ -> pr "This function returns a string, or NULL on error. The string is owned by the guest handle and must I be freed.\n\n" + | RConstOptString _ -> + pr "This function returns a string which may be NULL. +There is way to return an error from this function. +The string is owned by the guest handle and must I be freed.\n\n" | RString _ -> pr "This function returns a string, or NULL on error. I.\n\n" @@ -3425,11 +3716,18 @@ strings, or NULL if there was an error. The array of strings will always have length C<2n+1>, where C keys and values alternate, followed by the trailing NULL entry. I.\n\n" + | RBufferOut _ -> + pr "This function returns a buffer, or NULL on error. +The size of the returned buffer is written to C<*size_r>. +I.\n\n" ); if List.mem ProtocolLimitWarning flags then pr "%s\n\n" protocol_limit_warning; if List.mem DangerWillRobinson flags then - pr "%s\n\n" danger_will_robinson + pr "%s\n\n" danger_will_robinson; + match deprecation_notice flags with + | None -> () + | Some txt -> pr "%s\n\n" txt ) ) all_functions_sorted @@ -3448,6 +3746,10 @@ and generate_structs_pod () = | name, (FUInt64|FBytes) -> pr " uint64_t %s;\n" name | name, FInt64 -> pr " int64_t %s;\n" name | name, FString -> pr " char *%s;\n" name + | name, FBuffer -> + pr " /* The next two fields describe a byte array. */\n"; + pr " uint32_t %s_len;\n" name; + pr " char *%s;\n" name | name, FUUID -> pr " /* The next field is NOT nul-terminated, be careful when printing it: */\n"; pr " char %s[32];\n" name @@ -3491,6 +3793,7 @@ and generate_xdr () = List.iter (function | name, FChar -> pr " char %s;\n" name | name, FString -> pr " string %s<>;\n" name + | name, FBuffer -> pr " opaque %s<>;\n" name | name, FUUID -> pr " opaque %s[32];\n" name | name, (FInt32|FUInt32) -> pr " int %s;\n" name | name, (FInt64|FUInt64|FBytes) -> pr " hyper %s;\n" name @@ -3535,8 +3838,8 @@ and generate_xdr () = pr "struct %s_ret {\n" name; pr " bool %s;\n" n; pr "};\n\n" - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" + | RConstString _ | RConstOptString _ -> + failwithf "RConstString|RConstOptString cannot be used by daemon functions" | RString n -> pr "struct %s_ret {\n" name; pr " string %s<>;\n" n; @@ -3557,6 +3860,10 @@ and generate_xdr () = pr "struct %s_ret {\n" name; pr " str %s<>;\n" n; pr "};\n\n" + | RBufferOut n -> + pr "struct %s_ret {\n" name; + pr " opaque %s<>;\n" n; + pr "};\n\n" ); ) daemon_functions; @@ -3652,6 +3959,9 @@ and generate_structs_h () = function | name, FChar -> pr " char %s;\n" name | name, FString -> pr " char *%s;\n" name + | name, FBuffer -> + pr " uint32_t %s_len;\n" name; + pr " char *%s;\n" name | name, FUUID -> pr " char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name | name, FUInt32 -> pr " uint32_t %s;\n" name | name, FInt32 -> pr " int32_t %s;\n" name @@ -3737,7 +4047,7 @@ check_state (guestfs_h *g, const char *caller) { if (!guestfs_is_ready (g)) { if (guestfs_is_config (g)) - error (g, \"%%s: call launch() before using this function\", + error (g, \"%%s: call launch before using this function\\n(in guestfish, don't forget to use the 'run' command)\", caller); else if (guestfs_is_launching (g)) error (g, \"%%s: call wait_ready() before using this function\", @@ -3770,12 +4080,12 @@ check_state (guestfs_h *g, const char *caller) pr " struct guestfs_message_error err;\n"; (match fst style with | RErr -> () - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" + | RConstString _ | RConstOptString _ -> + failwithf "RConstString|RConstOptString cannot be used by daemon functions" | RInt _ | RInt64 _ | RBool _ | RString _ | RStringList _ | RStruct _ | RStructList _ - | RHashtable _ -> + | RHashtable _ | RBufferOut _ -> pr " struct %s_ret ret;\n" name ); pr "};\n"; @@ -3811,12 +4121,12 @@ check_state (guestfs_h *g, const char *caller) (match fst style with | RErr -> () - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" + | RConstString _ | RConstOptString _ -> + failwithf "RConstString|RConstOptString cannot be used by daemon functions" | RInt _ | RInt64 _ | RBool _ | RString _ | RStringList _ | RStruct _ | RStructList _ - | RHashtable _ -> + | RHashtable _ | RBufferOut _ -> pr " if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name; pr " error (g, \"%%s: failed to parse reply\", \"%s\");\n" name; pr " return;\n"; @@ -3834,11 +4144,11 @@ check_state (guestfs_h *g, const char *caller) let error_code = match fst style with | RErr | RInt _ | RInt64 _ | RBool _ -> "-1" - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" + | RConstString _ | RConstOptString _ -> + failwithf "RConstString|RConstOptString cannot be used by daemon functions" | RString _ | RStringList _ | RStruct _ | RStructList _ - | RHashtable _ -> + | RHashtable _ | RBufferOut _ -> "NULL" in pr "{\n"; @@ -3958,8 +4268,8 @@ check_state (guestfs_h *g, const char *caller) | RErr -> pr " return 0;\n" | RInt n | RInt64 n | RBool n -> pr " return ctx.ret.%s;\n" n - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" + | RConstString _ | RConstOptString _ -> + failwithf "RConstString|RConstOptString cannot be used by daemon functions" | RString n -> pr " return ctx.ret.%s; /* caller will free */\n" n | RStringList n | RHashtable n -> @@ -3976,6 +4286,9 @@ check_state (guestfs_h *g, const char *caller) | RStructList (n, _) -> pr " /* caller will free this */\n"; pr " return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n + | RBufferOut n -> + pr " *size_r = ctx.ret.%s.%s_len;\n" n n; + pr " return ctx.ret.%s.%s_val; /* caller will free */\n" n n ); pr "}\n\n" @@ -4051,12 +4364,16 @@ and generate_daemon_actions () = | RErr | RInt _ -> pr " int r;\n"; "-1" | RInt64 _ -> pr " int64_t r;\n"; "-1" | RBool _ -> pr " int r;\n"; "-1" - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" + | RConstString _ | RConstOptString _ -> + failwithf "RConstString|RConstOptString cannot be used by daemon functions" | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ | RHashtable _ -> pr " char **r;\n"; "NULL" | RStruct (_, typ) -> pr " guestfs_int_%s *r;\n" typ; "NULL" - | RStructList (_, typ) -> pr " guestfs_int_%s_list *r;\n" typ; "NULL" in + | RStructList (_, typ) -> pr " guestfs_int_%s_list *r;\n" typ; "NULL" + | RBufferOut _ -> + pr " size_t size;\n"; + pr " char *r;\n"; + "NULL" in (match snd style with | [] -> () @@ -4110,11 +4427,11 @@ and generate_daemon_actions () = (* Don't want to call the impl with any FileIn or FileOut * parameters, since these go "outside" the RPC protocol. *) - let argsnofile = + let args' = List.filter (function FileIn _ | FileOut _ -> false | _ -> true) (snd style) in pr " r = do_%s " name; - generate_call_args argsnofile; + generate_c_call_args (fst style, args'); pr ";\n"; pr " if (r == %s)\n" error_code; @@ -4137,8 +4454,8 @@ and generate_daemon_actions () = pr " ret.%s = r;\n" n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" + | RConstString _ | RConstOptString _ -> + failwithf "RConstString|RConstOptString cannot be used by daemon functions" | RString n -> pr " struct guestfs_%s_ret ret;\n" name; pr " ret.%s = r;\n" n; @@ -4166,6 +4483,13 @@ and generate_daemon_actions () = name; pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name + | RBufferOut n -> + pr " struct guestfs_%s_ret ret;\n" name; + pr " ret.%s.%s_val = r;\n" n n; + pr " ret.%s.%s_len = size;\n" n n; + pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" + name; + pr " free (r);\n" ); (* Free the args. *) @@ -4269,7 +4593,7 @@ and generate_daemon_actions () = pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name; pr " return -1;\n"; pr " }\n"; - | FInt32 | FUInt32 | FUInt64 | FChar -> + | FBuffer | FInt32 | FUInt32 | FUInt64 | FChar -> assert false (* can never be an LVM column *) ); pr " tok = next;\n"; @@ -4710,6 +5034,13 @@ and generate_one_test_body name i test_name init test = ["lvcreate"; "LV"; "VG"; "8"]; ["mkfs"; "ext2"; "/dev/VG/LV"]; ["mount"; "/dev/VG/LV"; "/"]] + | InitSquashFS -> + pr " /* InitSquashFS for %s */\n" test_name; + List.iter (generate_test_command_call test_name) + [["blockdev_setrw"; "/dev/sda"]; + ["umount_all"]; + ["lvm_remove_all"]; + ["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]] ); let get_seq_last = function @@ -4865,6 +5196,23 @@ and generate_one_test_body name i test_name init test = in List.iter (generate_test_command_call test_name) seq; generate_test_command_call ~test test_name last + | TestOutputBuffer (seq, expected) -> + pr " /* TestOutputBuffer for %s (%d) */\n" name i; + pr " const char *expected = \"%s\";\n" (c_quote expected); + let seq, last = get_seq_last seq in + let len = String.length expected in + let test () = + pr " if (size != %d) {\n" len; + pr " fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len; + pr " return -1;\n"; + pr " }\n"; + pr " if (strncmp (r, expected, size) != 0) {\n"; + pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last | TestOutputStruct (seq, checks) -> pr " /* TestOutputStruct for %s (%d) */\n" name i; let seq, last = get_seq_last seq in @@ -4965,7 +5313,8 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = 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" + | RConstString _ | RConstOptString _ -> + pr " const char *r;\n"; "NULL" | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ | RHashtable _ -> pr " char **r;\n"; @@ -4974,7 +5323,11 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | RStruct (_, typ) -> pr " struct guestfs_%s *r;\n" typ; "NULL" | RStructList (_, typ) -> - pr " struct guestfs_%s_list *r;\n" typ; "NULL" in + pr " struct guestfs_%s_list *r;\n" typ; "NULL" + | RBufferOut _ -> + pr " char *r;\n"; + pr " size_t size;\n"; + "NULL" in pr " suppress_error = %d;\n" (if expect_error then 1 else 0); pr " r = guestfs_%s (g" name; @@ -5000,7 +5353,13 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = let b = bool_of_string arg in pr ", %d" (if b then 1 else 0) ) (List.combine (snd style) args); + (match fst style with + | RBufferOut _ -> pr ", &size" + | _ -> () + ); + pr ");\n"; + if not expect_error then pr " if (r == %s)\n" error_code else @@ -5014,8 +5373,9 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = ); (match fst style with - | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _ -> () - | RString _ -> pr " free (r);\n" + | RErr | RInt _ | RInt64 _ | RBool _ + | RConstString _ | RConstOptString _ -> () + | RString _ | RBufferOut _ -> pr " free (r);\n" | RStringList _ | RHashtable _ -> pr " for (i = 0; r[i] != NULL; ++i)\n"; pr " free (r[i]);\n"; @@ -5052,6 +5412,7 @@ and generate_fish_cmds () = pr "#include \n"; pr "#include \n"; pr "#include \n"; + pr "#include \n"; pr "\n"; pr "#include \n"; pr "#include \"fish.h\"\n"; @@ -5104,6 +5465,12 @@ and generate_fish_cmds () = ("\n\n" ^ danger_will_robinson) else "" in + let warnings = + warnings ^ + match deprecation_notice flags with + | None -> "" + | Some txt -> "\n\n" ^ txt in + let describe_alias = if name <> alias then sprintf "\n\nYou can use '%s' as an alias for this command." alias @@ -5129,7 +5496,7 @@ and generate_fish_cmds () = List.iter ( fun (typ, cols) -> let needs_i = - List.exists (function (_, FUUID) -> true | _ -> false) cols in + List.exists (function (_, (FUUID|FBuffer)) -> true | _ -> false) cols in pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ; pr "{\n"; @@ -5146,6 +5513,14 @@ and generate_fish_cmds () = pr " for (i = 0; i < 32; ++i)\n"; pr " printf (\"%%c\", %s->%s[i]);\n" typ name; pr " printf (\"\\n\");\n" + | name, FBuffer -> + pr " printf (\"%s: \");\n" name; + pr " for (i = 0; i < %s->%s_len; ++i)\n" typ name; + pr " if (isprint (%s->%s[i]))\n" typ name; + pr " printf (\"%%c\", %s->%s[i]);\n" typ name; + pr " else\n"; + pr " printf (\"\\\\x%%02x\", %s->%s[i]);\n" typ name; + pr " printf (\"\\n\");\n" | name, (FUInt64|FBytes) -> pr " printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name | name, FInt64 -> @@ -5184,11 +5559,14 @@ and generate_fish_cmds () = | RInt _ | RBool _ -> pr " int r;\n" | RInt64 _ -> pr " int64_t r;\n" - | RConstString _ -> pr " const char *r;\n" + | RConstString _ | RConstOptString _ -> pr " const char *r;\n" | RString _ -> pr " char *r;\n" | RStringList _ | RHashtable _ -> pr " char **r;\n" | RStruct (_, typ) -> pr " struct guestfs_%s *r;\n" typ | RStructList (_, typ) -> pr " struct guestfs_%s_list *r;\n" typ + | RBufferOut _ -> + pr " char *r;\n"; + pr " size_t size;\n"; ); List.iter ( function @@ -5235,7 +5613,7 @@ and generate_fish_cmds () = try find_map (function FishAction n -> Some n | _ -> None) flags with Not_found -> sprintf "guestfs_%s" name in pr " r = %s " fn; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; (* Check return value for errors and display command results. *) @@ -5257,6 +5635,9 @@ and generate_fish_cmds () = pr " if (r == NULL) return -1;\n"; pr " printf (\"%%s\\n\", r);\n"; pr " return 0;\n" + | RConstOptString _ -> + pr " printf (\"%%s\\n\", r ? : \"(null)\");\n"; + pr " return 0;\n" | RString _ -> pr " if (r == NULL) return -1;\n"; pr " printf (\"%%s\\n\", r);\n"; @@ -5282,6 +5663,11 @@ and generate_fish_cmds () = pr " print_table (r);\n"; pr " free_strings (r);\n"; pr " return 0;\n" + | RBufferOut _ -> + pr " if (r == NULL) return -1;\n"; + pr " fwrite (r, size, 1, stdout);\n"; + pr " free (r);\n"; + pr " return 0;\n" ); pr "}\n"; pr "\n" @@ -5456,7 +5842,11 @@ and generate_fish_actions_pod () = pr "%s\n\n" protocol_limit_warning; if List.mem DangerWillRobinson flags then - pr "%s\n\n" danger_will_robinson + pr "%s\n\n" danger_will_robinson; + + match deprecation_notice flags with + | None -> () + | Some txt -> pr "%s\n\n" txt ) all_functions_sorted (* Generate a C function prototype. *) @@ -5471,8 +5861,8 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) | RInt _ -> pr "int " | RInt64 _ -> pr "int64_t " | RBool _ -> pr "int " - | RConstString _ -> pr "const char *" - | RString _ -> pr "char *" + | RConstString _ | RConstOptString _ -> pr "const char *" + | RString _ | RBufferOut _ -> pr "char *" | RStringList _ | RHashtable _ -> pr "char **" | RStruct (_, typ) -> if not in_daemon then pr "struct guestfs_%s *" typ @@ -5481,8 +5871,9 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) if not in_daemon then pr "struct guestfs_%s_list *" typ else pr "guestfs_int_%s_list *" typ ); + let is_RBufferOut = match fst style with RBufferOut _ -> true | _ -> false in pr "%s%s (" prefix name; - if handle = None && List.length (snd style) = 0 then + if handle = None && List.length (snd style) = 0 && not is_RBufferOut then pr "void" else ( let comma = ref false in @@ -5513,25 +5904,37 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) | FileOut n -> if not in_daemon then (next (); pr "const char *%s" n) ) (snd style); + if is_RBufferOut then (next (); pr "size_t *size_r"); ); pr ")"; if semicolon then pr ";"; if newline then pr "\n" (* Generate C call arguments, eg "(handle, foo, bar)" *) -and generate_call_args ?handle args = +and generate_c_call_args ?handle ?(decl = false) style = pr "("; let comma = ref false in + let next () = + if !comma then pr ", "; + comma := true + in (match handle with | None -> () | Some handle -> pr "%s" handle; comma := true ); List.iter ( fun arg -> - if !comma then pr ", "; - comma := true; + next (); pr "%s" (name_of_argt arg) - ) args; + ) (snd style); + (* For RBufferOut calls, add implicit &size parameter. *) + if not decl then ( + match fst style with + | RBufferOut _ -> + next (); + pr "&size" + | _ -> () + ); pr ")" (* Generate the OCaml bindings interface. *) @@ -5661,6 +6064,10 @@ copy_table (char * const * argv) (match col with | name, FString -> pr " v = caml_copy_string (%s->%s);\n" typ name + | name, FBuffer -> + pr " v = caml_alloc_string (%s->%s_len);\n" typ name; + pr " memcpy (String_val (v), %s->%s, %s->%s_len);\n" + typ name typ name | name, FUUID -> pr " v = caml_alloc_string (32);\n"; pr " memcpy (String_val (v), %s->%s, 32);\n" typ name @@ -5712,6 +6119,9 @@ copy_table (char * const * argv) let params = "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in + let needs_extra_vs = + match fst style with RConstOptString _ -> true | _ -> false in + pr "CAMLprim value\n"; pr "ocaml_guestfs_%s (value %s" name (List.hd params); List.iter (pr ", value %s") (List.tl params); @@ -5728,7 +6138,10 @@ copy_table (char * const * argv) | ps -> pr " CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps) ); - pr " CAMLlocal1 (rv);\n"; + if not needs_extra_vs then + pr " CAMLlocal1 (rv);\n" + else + pr " CAMLlocal3 (rv, v, v2);\n"; pr "\n"; pr " guestfs_h *g = Guestfs_val (gv);\n"; @@ -5759,7 +6172,8 @@ copy_table (char * const * argv) | RInt _ -> pr " int r;\n"; "-1" | RInt64 _ -> pr " int64_t r;\n"; "-1" | RBool _ -> pr " int r;\n"; "-1" - | RConstString _ -> pr " const char *r;\n"; "NULL" + | RConstString _ | RConstOptString _ -> + pr " const char *r;\n"; "NULL" | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ -> pr " int i;\n"; @@ -5772,12 +6186,16 @@ copy_table (char * const * argv) | RHashtable _ -> pr " int i;\n"; pr " char **r;\n"; + "NULL" + | RBufferOut _ -> + pr " char *r;\n"; + pr " size_t size;\n"; "NULL" in pr "\n"; pr " caml_enter_blocking_section ();\n"; pr " r = guestfs_%s " name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; pr " caml_leave_blocking_section ();\n"; @@ -5798,7 +6216,15 @@ copy_table (char * const * argv) | RInt64 _ -> pr " rv = caml_copy_int64 (r);\n" | RBool _ -> pr " rv = Val_bool (r);\n" - | RConstString _ -> pr " rv = caml_copy_string (r);\n" + | RConstString _ -> + pr " rv = caml_copy_string (r);\n" + | RConstOptString _ -> + pr " if (r) { /* Some string */\n"; + pr " v = caml_alloc (1, 0);\n"; + pr " v2 = caml_copy_string (r);\n"; + pr " Store_field (v, 0, v2);\n"; + pr " } else /* None */\n"; + pr " v = Val_int (0);\n"; | RString _ -> pr " rv = caml_copy_string (r);\n"; pr " free (r);\n" @@ -5816,6 +6242,9 @@ copy_table (char * const * argv) pr " rv = copy_table (r);\n"; pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n"; pr " free (r);\n"; + | RBufferOut _ -> + pr " rv = caml_alloc_string (size);\n"; + pr " memcpy (String_val (rv), r, size);\n"; ); pr " CAMLreturn (rv);\n"; @@ -5841,6 +6270,7 @@ and generate_ocaml_structure_decls () = List.iter ( function | name, FString -> pr " %s : string;\n" name + | name, FBuffer -> pr " %s : string;\n" name | name, FUUID -> pr " %s : string;\n" name | name, (FBytes|FInt64|FUInt64) -> pr " %s : int64;\n" name | name, (FInt32|FUInt32) -> pr " %s : int32;\n" name @@ -5868,7 +6298,8 @@ and generate_ocaml_prototype ?(is_external = false) name style = | RInt64 _ -> pr "int64" | RBool _ -> pr "bool" | RConstString _ -> pr "string" - | RString _ -> pr "string" + | RConstOptString _ -> pr "string option" + | RString _ | RBufferOut _ -> pr "string" | RStringList _ -> pr "string array" | RStruct (_, typ) -> pr "%s" typ | RStructList (_, typ) -> pr "%s array" typ @@ -5984,7 +6415,9 @@ DESTROY (g) | RInt64 _ -> pr "SV *\n" | RBool _ -> pr "SV *\n" | RConstString _ -> pr "SV *\n" + | RConstOptString _ -> pr "SV *\n" | RString _ -> pr "SV *\n" + | RBufferOut _ -> pr "SV *\n" | RStringList _ | RStruct _ | RStructList _ | RHashtable _ -> @@ -5992,7 +6425,7 @@ DESTROY (g) ); (* Call and arguments. *) pr "%s " name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" ~decl:true style; pr "\n"; pr " guestfs_h *g;\n"; iteri ( @@ -6026,7 +6459,7 @@ DESTROY (g) pr " int r;\n"; pr " PPCODE:\n"; pr " r = guestfs_%s " name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (r == -1)\n"; @@ -6037,7 +6470,7 @@ DESTROY (g) pr " int %s;\n" n; pr " CODE:\n"; pr " %s = guestfs_%s " n name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (%s == -1)\n" n; @@ -6050,7 +6483,7 @@ DESTROY (g) pr " int64_t %s;\n" n; pr " CODE:\n"; pr " %s = guestfs_%s " n name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (%s == -1)\n" n; @@ -6063,7 +6496,7 @@ DESTROY (g) pr " const char *%s;\n" n; pr " CODE:\n"; pr " %s = guestfs_%s " n name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (%s == NULL)\n" n; @@ -6071,12 +6504,26 @@ DESTROY (g) pr " RETVAL = newSVpv (%s, 0);\n" n; pr " OUTPUT:\n"; pr " RETVAL\n" + | RConstOptString n -> + pr "PREINIT:\n"; + pr " const char *%s;\n" n; + pr " CODE:\n"; + pr " %s = guestfs_%s " n name; + generate_c_call_args ~handle:"g" style; + pr ";\n"; + do_cleanups (); + pr " if (%s == NULL)\n" n; + pr " RETVAL = &PL_sv_undef;\n"; + pr " else\n"; + pr " RETVAL = newSVpv (%s, 0);\n" n; + pr " OUTPUT:\n"; + pr " RETVAL\n" | RString n -> pr "PREINIT:\n"; pr " char *%s;\n" n; pr " CODE:\n"; pr " %s = guestfs_%s " n name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (%s == NULL)\n" n; @@ -6091,7 +6538,7 @@ DESTROY (g) pr " int i, n;\n"; pr " PPCODE:\n"; pr " %s = guestfs_%s " n name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (%s == NULL)\n" n; @@ -6109,6 +6556,21 @@ DESTROY (g) | RStructList (n, typ) -> let cols = cols_of_struct typ in generate_perl_struct_list_code typ cols name style n do_cleanups + | RBufferOut n -> + pr "PREINIT:\n"; + pr " char *%s;\n" n; + pr " size_t size;\n"; + pr " CODE:\n"; + pr " %s = guestfs_%s " n name; + generate_c_call_args ~handle:"g" style; + pr ";\n"; + do_cleanups (); + pr " if (%s == NULL)\n" n; + pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name; + pr " RETVAL = newSVpv (%s, size);\n" n; + pr " free (%s);\n" n; + pr " OUTPUT:\n"; + pr " RETVAL\n" ); pr "\n" @@ -6121,7 +6583,7 @@ and generate_perl_struct_list_code typ cols name style n do_cleanups = pr " HV *hv;\n"; pr " PPCODE:\n"; pr " %s = guestfs_%s " n name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (%s == NULL)\n" n; @@ -6137,6 +6599,9 @@ and generate_perl_struct_list_code typ cols name style n do_cleanups = | name, FUUID -> pr " (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 32), 0);\n" name (String.length name) n name + | name, FBuffer -> + pr " (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, %s->val[i].%s_len), 0);\n" + name (String.length name) n name n name | name, (FBytes|FUInt64) -> pr " (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n" name (String.length name) n name @@ -6162,7 +6627,7 @@ and generate_perl_struct_code typ cols name style n do_cleanups = pr " struct guestfs_%s *%s;\n" typ n; pr " PPCODE:\n"; pr " %s = guestfs_%s " n name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; do_cleanups (); pr " if (%s == NULL)\n" n; @@ -6176,6 +6641,9 @@ and generate_perl_struct_code typ cols name style n do_cleanups = | name, FString -> pr " PUSHs (sv_2mortal (newSVpv (%s->%s, 0)));\n" n name + | name, FBuffer -> + pr " PUSHs (sv_2mortal (newSVpv (%s->%s, %s->%s_len)));\n" + n name n name | name, FUUID -> pr " PUSHs (sv_2mortal (newSVpv (%s->%s, 32)));\n" n name @@ -6295,7 +6763,10 @@ sub new { if List.mem ProtocolLimitWarning flags then pr "%s\n\n" protocol_limit_warning; if List.mem DangerWillRobinson flags then - pr "%s\n\n" danger_will_robinson + pr "%s\n\n" danger_will_robinson; + match deprecation_notice flags with + | None -> () + | Some txt -> pr "%s\n\n" txt ) ) all_functions_sorted; @@ -6332,7 +6803,9 @@ and generate_perl_prototype name style = | RInt n | RInt64 n | RConstString n - | RString n -> pr "$%s = " n + | RConstOptString n + | RString n + | RBufferOut n -> pr "$%s = " n | RStruct (n,_) | RHashtable n -> pr "%%%s = " n | RStringList n @@ -6508,6 +6981,10 @@ py_guestfs_close (PyObject *self, PyObject *args) pr " PyDict_SetItemString (dict, \"%s\",\n" name; pr " PyString_FromString (%s->%s));\n" typ name + | name, FBuffer -> + pr " PyDict_SetItemString (dict, \"%s\",\n" name; + pr " PyString_FromStringAndSize (%s->%s, %s->%s_len));\n" + typ name typ name | name, FUUID -> pr " PyDict_SetItemString (dict, \"%s\",\n" name; pr " PyString_FromStringAndSize (%s->%s, 32));\n" @@ -6574,12 +7051,17 @@ py_guestfs_close (PyObject *self, PyObject *args) 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" + | RConstString _ | RConstOptString _ -> + pr " const char *r;\n"; "NULL" | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ | RHashtable _ -> pr " char **r;\n"; "NULL" | RStruct (_, typ) -> pr " struct guestfs_%s *r;\n" typ; "NULL" | RStructList (_, typ) -> - pr " struct guestfs_%s_list *r;\n" typ; "NULL" in + pr " struct guestfs_%s_list *r;\n" typ; "NULL" + | RBufferOut _ -> + pr " char *r;\n"; + pr " size_t size;\n"; + "NULL" in List.iter ( function @@ -6630,7 +7112,7 @@ py_guestfs_close (PyObject *self, PyObject *args) pr "\n"; pr " r = guestfs_%s " name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; List.iter ( @@ -6654,6 +7136,13 @@ py_guestfs_close (PyObject *self, PyObject *args) | RBool _ -> pr " py_r = PyInt_FromLong ((long) r);\n" | RInt64 _ -> pr " py_r = PyLong_FromLongLong (r);\n" | RConstString _ -> pr " py_r = PyString_FromString (r);\n" + | RConstOptString _ -> + pr " if (r)\n"; + pr " py_r = PyString_FromString (r);\n"; + pr " else {\n"; + pr " Py_INCREF (Py_None);\n"; + pr " py_r = Py_None;\n"; + pr " }\n" | RString _ -> pr " py_r = PyString_FromString (r);\n"; pr " free (r);\n" @@ -6669,6 +7158,9 @@ py_guestfs_close (PyObject *self, PyObject *args) | RHashtable n -> pr " py_r = put_table (r);\n"; pr " free_strings (r);\n" + | RBufferOut _ -> + pr " py_r = PyString_FromStringAndSize (r, size);\n"; + pr " free (r);\n" ); pr " return py_r;\n"; @@ -6772,15 +7264,16 @@ class GuestFS: List.iter ( fun (name, style, _, flags, _, _, longdesc) -> pr " def %s " name; - generate_call_args ~handle:"self" (snd style); + generate_py_call_args ~handle:"self" (snd style); pr ":\n"; if not (List.mem NotInDocs flags) then ( let doc = replace_str longdesc "C doc + | RErr | RInt _ | RInt64 _ | RBool _ + | RConstOptString _ | RConstString _ + | RString _ | RBufferOut _ -> doc | RStringList _ -> doc ^ "\n\nThis function returns a list of strings." | RStruct (_, typ) -> @@ -6797,17 +7290,27 @@ class GuestFS: if List.mem DangerWillRobinson flags then doc ^ "\n\n" ^ danger_will_robinson else doc in + let doc = + match deprecation_notice flags with + | None -> doc + | Some txt -> doc ^ "\n\n" ^ txt 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 " u\"\"\"%s\"\"\"\n" doc; ); pr " return libguestfsmod.%s " name; - generate_call_args ~handle:"self._o" (snd style); + generate_py_call_args ~handle:"self._o" (snd style); pr "\n"; pr "\n"; ) all_functions +(* Generate Python call arguments, eg "(handle, foo, bar)" *) +and generate_py_call_args ~handle args = + pr "(%s" handle; + List.iter (fun arg -> pr ", %s" (name_of_argt arg)) args; + pr ")" + (* Useful if you need the longdesc POD text as plain text. Returns a * list of lines. * @@ -6955,16 +7458,21 @@ static VALUE ruby_guestfs_close (VALUE gv) 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" + | RConstString _ | RConstOptString _ -> + pr " const char *r;\n"; "NULL" | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ | RHashtable _ -> pr " char **r;\n"; "NULL" | RStruct (_, typ) -> pr " struct guestfs_%s *r;\n" typ; "NULL" | RStructList (_, typ) -> - pr " struct guestfs_%s_list *r;\n" typ; "NULL" in + pr " struct guestfs_%s_list *r;\n" typ; "NULL" + | RBufferOut _ -> + pr " char *r;\n"; + pr " size_t size;\n"; + "NULL" in pr "\n"; pr " r = guestfs_%s " name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; List.iter ( @@ -6987,6 +7495,11 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " return ULL2NUM (r);\n" | RConstString _ -> pr " return rb_str_new2 (r);\n"; + | RConstOptString _ -> + pr " if (r)\n"; + pr " return rb_str_new2 (r);\n"; + pr " else\n"; + pr " return Qnil;\n"; | RString _ -> pr " VALUE rv = rb_str_new2 (r);\n"; pr " free (r);\n"; @@ -7017,6 +7530,10 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " }\n"; pr " free (r);\n"; pr " return rv;\n" + | RBufferOut _ -> + pr " VALUE rv = rb_str_new (r, size);\n"; + pr " free (r);\n"; + pr " return rv;\n"; ); pr "}\n"; @@ -7051,6 +7568,8 @@ and generate_ruby_struct_code typ cols = function | name, FString -> pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name + | name, FBuffer -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name | name, FUUID -> pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, 32));\n" name name | name, (FBytes|FUInt64) -> @@ -7079,6 +7598,8 @@ and generate_ruby_struct_list_code typ cols = function | name, FString -> pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name + | name, FBuffer -> + 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 | name, FUUID -> pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name | name, (FBytes|FUInt64) -> @@ -7181,6 +7702,10 @@ public class GuestFS { if List.mem DangerWillRobinson flags then doc ^ "\n\n" ^ danger_will_robinson else doc in + let doc = + match deprecation_notice flags with + | None -> doc + | Some txt -> doc ^ "\n\n" ^ txt in let doc = pod2text ~width:60 name doc in let doc = List.map ( (* RHBZ#501883 *) function @@ -7206,7 +7731,7 @@ public class GuestFS { pr " "; if fst style <> RErr then pr "return "; pr "_%s " name; - generate_call_args ~handle:"g" (snd style); + generate_java_call_args ~handle:"g" (snd style); pr ";\n"; pr " }\n"; pr " "; @@ -7217,6 +7742,12 @@ public class GuestFS { pr "}\n" +(* Generate Java call arguments, eg "(handle, foo, bar)" *) +and generate_java_call_args ~handle args = + pr "(%s" handle; + List.iter (fun arg -> pr ", %s" (name_of_argt arg)) args; + pr ")" + and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false) ?(semicolon=true) name style = if privat then pr "private "; @@ -7229,7 +7760,8 @@ and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false) | RInt _ -> pr "int "; | RInt64 _ -> pr "long "; | RBool _ -> pr "boolean "; - | RConstString _ | RString _ -> pr "String "; + | RConstString _ | RConstOptString _ | RString _ + | RBufferOut _ -> pr "String "; | RStringList _ -> pr "String[] "; | RStruct (_, typ) -> let name = java_name_of_struct typ in @@ -7290,7 +7822,8 @@ public class %s { List.iter ( function | name, FString - | name, FUUID -> pr " public String %s;\n" name + | name, FUUID + | name, FBuffer -> pr " public String %s;\n" name | name, (FBytes|FUInt64|FInt64) -> pr " public long %s;\n" name | name, (FUInt32|FInt32) -> pr " public int %s;\n" name | name, FChar -> pr " public char %s;\n" name @@ -7357,7 +7890,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | RInt _ -> pr "jint "; | RInt64 _ -> pr "jlong "; | RBool _ -> pr "jboolean "; - | RConstString _ | RString _ -> pr "jstring "; + | RConstString _ | RConstOptString _ | RString _ + | RBufferOut _ -> pr "jstring "; | RStruct _ | RHashtable _ -> pr "jobject "; | RStringList _ | RStructList _ -> @@ -7392,6 +7926,7 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | RInt _ -> pr " int r;\n"; "-1", "0" | RInt64 _ -> pr " int64_t r;\n"; "-1", "0" | RConstString _ -> pr " const char *r;\n"; "NULL", "NULL" + | RConstOptString _ -> pr " const char *r;\n"; "NULL", "NULL" | RString _ -> pr " jstring jr;\n"; pr " char *r;\n"; "NULL", "NULL" @@ -7412,7 +7947,12 @@ Java_com_redhat_et_libguestfs_GuestFS__1close pr " jfieldID fl;\n"; pr " jobject jfl;\n"; pr " struct guestfs_%s_list *r;\n" typ; "NULL", "NULL" - | RHashtable _ -> pr " char **r;\n"; "NULL", "NULL" in + | RHashtable _ -> pr " char **r;\n"; "NULL", "NULL" + | RBufferOut _ -> + pr " jstring jr;\n"; + pr " char *r;\n"; + pr " size_t size;\n"; + "NULL", "NULL" in List.iter ( function | String n @@ -7432,7 +7972,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close (match fst style with | RStringList _ | RStructList _ -> true | RErr | RBool _ | RInt _ | RInt64 _ | RConstString _ - | RString _ | RStruct _ | RHashtable _ -> false) || + | RConstOptString _ + | RString _ | RBufferOut _ | RStruct _ | RHashtable _ -> false) || List.exists (function StringList _ -> true | _ -> false) (snd style) in if needs_i then pr " int i;\n"; @@ -7467,7 +8008,7 @@ Java_com_redhat_et_libguestfs_GuestFS__1close (* Make the call. *) pr " r = guestfs_%s " name; - generate_call_args ~handle:"g" (snd style); + generate_c_call_args ~handle:"g" style; pr ";\n"; (* Release the parameters. *) @@ -7504,6 +8045,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | RBool _ -> pr " return (jboolean) r;\n" | RInt64 _ -> pr " return (jlong) r;\n" | RConstString _ -> pr " return (*env)->NewStringUTF (env, r);\n" + | RConstOptString _ -> + pr " return (*env)->NewStringUTF (env, r); /* XXX r NULL? */\n" | RString _ -> pr " jr = (*env)->NewStringUTF (env, r);\n"; pr " free (r);\n"; @@ -7532,6 +8075,10 @@ Java_com_redhat_et_libguestfs_GuestFS__1close (* XXX *) pr " throw_exception (env, \"%s: internal error: please let us know how to make a Java HashMap from JNI bindings!\");\n" name; pr " return NULL;\n" + | RBufferOut _ -> + pr " jr = (*env)->NewStringUTF (env, r); /* XXX size */\n"; + pr " free (r);\n"; + pr " return jr;\n" ); pr "}\n"; @@ -7554,6 +8101,15 @@ and generate_java_struct_return typ jtyp cols = pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; pr " (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n"; pr " }\n"; + | name, FBuffer -> + pr " {\n"; + pr " int len = r->%s_len;\n" name; + pr " char s[len+1];\n"; + pr " memcpy (s, r->%s, len);\n" name; + pr " s[len] = 0;\n"; + pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; + pr " (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n"; + pr " }\n"; | name, (FBytes|FUInt64|FInt64) -> pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name; pr " (*env)->SetLongField (env, jr, fl, r->%s);\n" name; @@ -7588,6 +8144,15 @@ and generate_java_struct_list_return typ jtyp cols = pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; pr " (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n"; pr " }\n"; + | name, FBuffer -> + pr " {\n"; + pr " int len = r->val[i].%s_len;\n" name; + pr " char s[len+1];\n"; + pr " memcpy (s, r->val[i].%s, len);\n" name; + pr " s[len] = 0;\n"; + pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; + pr " (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n"; + pr " }\n"; | name, (FBytes|FUInt64|FInt64) -> pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name; pr " (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name; @@ -7619,11 +8184,13 @@ and generate_haskell_hs () = | RInt64 _, _ -> true | RBool _, _ | RConstString _, _ + | RConstOptString _, _ | RString _, _ | RStringList _, _ | RStruct _, _ | RStructList _, _ - | RHashtable _, _ -> false in + | RHashtable _, _ + | RBufferOut _, _ -> false in pr "\ {-# INCLUDE #-} @@ -7732,8 +8299,9 @@ last_error h = do pr " then do\n"; pr " err <- last_error h\n"; pr " fail err\n"; - | RConstString _ | RString _ | RStringList _ | RStruct _ - | RStructList _ | RHashtable _ -> + | RConstString _ | RConstOptString _ | RString _ + | RStringList _ | RStruct _ + | RStructList _ | RHashtable _ | RBufferOut _ -> pr " if (r == nullPtr)\n"; pr " then do\n"; pr " err <- last_error h\n"; @@ -7749,11 +8317,13 @@ last_error h = do | RBool _ -> pr " else return (toBool r)\n" | RConstString _ + | RConstOptString _ | RString _ | RStringList _ | RStruct _ | RStructList _ - | RHashtable _ -> + | RHashtable _ + | RBufferOut _ -> pr " else return ()\n" (* XXXXXXXXXXXXXXXXXXXX *) ); pr "\n"; @@ -7786,6 +8356,7 @@ and generate_haskell_prototype ~handle ?(hs = false) style = | RInt64 _ -> pr "%s" int64 | RBool _ -> pr "%s" bool | RConstString _ -> pr "%s" string + | RConstOptString _ -> pr "Maybe %s" string | RString _ -> pr "%s" string | RStringList _ -> pr "[%s]" string | RStruct (_, typ) -> @@ -7795,6 +8366,7 @@ and generate_haskell_prototype ~handle ?(hs = false) style = let name = java_name_of_struct typ in pr "[%s]" name | RHashtable _ -> pr "Hashtable" + | RBufferOut _ -> pr "%s" string ); pr ")" @@ -7876,7 +8448,8 @@ print_strings (char * const* const argv) pr " return r;\n" | RBool _ -> pr " return strcmp (val, \"true\") == 0;\n" - | RConstString _ -> + | RConstString _ + | RConstOptString _ -> (* Can't return the input string here. Return a static * string so we ensure we get a segfault if the caller * tries to free it. @@ -7918,6 +8491,8 @@ print_strings (char * const* const argv) pr " }\n"; pr " strs[n*2] = NULL;\n"; pr " return strs;\n" + | RBufferOut _ -> + pr " return strdup (val);\n" ); pr "}\n"; pr "\n" @@ -7930,10 +8505,11 @@ print_strings (char * const* const argv) (match fst style with | RErr | RInt _ | RInt64 _ | RBool _ -> pr " return -1;\n" - | RConstString _ + | RConstString _ | RConstOptString _ | RString _ | RStringList _ | RStruct _ | RStructList _ - | RHashtable _ -> + | RHashtable _ + | RBufferOut _ -> pr " return NULL;\n" ); pr "}\n"; @@ -8225,7 +8801,7 @@ let output_to filename = let () = check_functions (); - if not (Sys.file_exists "config.status") then ( + if not (Sys.file_exists "HACKING") then ( eprintf "\ You are probably running this from the wrong directory. Run it from the top source directory using the command