X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fgenerator.ml;h=9f019048bab86dcbc8eb7449e783fcc5c6475a82;hp=c32b0ed8e0c9da80c13190b03eba486eab7d37ae;hb=f6e36bf361c587e0dbb2f0c71f5d22a7cf7f4f42;hpb=212a55d483c2a20e61f42211c0c64aab3645cb09 diff --git a/src/generator.ml b/src/generator.ml index c32b0ed..9f01904 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -33,6 +33,7 @@ *) #load "unix.cma";; +#load "str.cma";; open Printf @@ -43,9 +44,15 @@ and ret = *) | RErr (* "RInt" as a return value means an int which is -1 for error - * or any value >= 0 on success. + * 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. *) @@ -68,6 +75,15 @@ and ret = (* Stat buffers. *) | RStat of string | RStatVFS of string + (* 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 + * value if one is available, or write a custom struct. Don't use + * this if the list could potentially be very long, since it is + * inefficient. Keys should be unique. NULLs are not permitted. + *) + | RHashtable of string + and args = argt list (* Function parameters, guestfs handle is implicit. *) (* Note in future we should allow a "variable args" parameter as @@ -83,6 +99,16 @@ and argt = | StringList of string(* list of strings (each string cannot be NULL) *) | Bool of string (* boolean *) | Int of string (* int (smallish ints, signed, <= 31 bits) *) + (* These are treated as filenames (simple string parameters) in + * the C API and bindings. But in the RPC protocol, we transfer + * the actual file content up to or down from the daemon. + * FileIn: local machine -> daemon (in request) + * FileOut: daemon -> local machine (in reply) + * In guestfish (only), the special name "-" means read from + * stdin or write to stdout. + *) + | FileIn of string + | FileOut of string type flags = | ProtocolLimitWarning (* display warning about protocol size limits *) @@ -90,6 +116,7 @@ type flags = | FishAlias of string (* provide an alias for this cmd in guestfish *) | 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 @@ -110,12 +137,30 @@ can easily destroy all your data>." * the virtual machine and block devices are reused between tests. * So don't try testing kill_subprocess :-x * - * Between each test we umount-all and lvm-remove-all (except InitNone). + * Between each test we blockdev-setrw, umount-all, lvm-remove-all. + * + * If the appliance is running an older Linux kernel (eg. RHEL 5) then + * devices are named /dev/hda etc. To cope with this, the test suite + * adds some hairly logic to detect this case, and then automagically + * replaces all strings which match "/dev/sd.*" with "/dev/hd.*". + * When writing test cases you shouldn't have to worry about this + * difference. * * Don't assume anything about the previous contents of the block * devices. Use 'Init*' to create some initial scenarios. + * + * You can add a prerequisite clause to any individual test. This + * is a run-time check, which, if it fails, causes the test to be + * skipped. Useful if testing a command which might not work on + * all variations of libguestfs builds. A test that has prerequisite + * of 'Always' is run unconditionally. + * + * In addition, packagers can skip individual tests by setting the + * environment variables: eg: + * SKIP_TEST__=1 SKIP_TEST_COMMAND_3=1 (skips test #3 of command) + * SKIP_TEST_=1 SKIP_TEST_ZEROFREE=1 (skips all zerofree tests) *) -type tests = (test_init * test) list +type tests = (test_init * test_prereq * test) list and test = (* Run the command sequence and just expect nothing to fail. *) | TestRun of seq @@ -159,6 +204,21 @@ and test_field_compare = | CompareFieldsIntEq of string * string | CompareFieldsStrEq of string * string +(* Test prerequisites. *) +and test_prereq = + (* Test always runs. *) + | Always + (* Test is currently disabled - eg. it fails, or it tests some + * unimplemented feature. + *) + | Disabled + (* 'string' is some C code (a function body) that should return + * true or false. The test will run if the code returns true. + *) + | If of string + (* As for 'If' but the test runs _unless_ the code returns true. *) + | Unless of string + (* Some initial scenarios for testing. *) and test_init = (* Do nothing, block devices could contain random stuff including @@ -194,7 +254,81 @@ and cmd = string list * Apart from that, long descriptions are just perldoc paragraphs. *) -let non_daemon_functions = [ +(* These test functions are used in the language binding tests. *) + +let test_all_args = [ + String "str"; + OptString "optstr"; + StringList "strlist"; + Bool "b"; + Int "integer"; + FileIn "filein"; + FileOut "fileout"; +] + +let test_all_rets = [ + (* except for RErr, which is tested thoroughly elsewhere *) + "test0rint", RInt "valout"; + "test0rint64", RInt64 "valout"; + "test0rbool", RBool "valout"; + "test0rconststring", RConstString "valout"; + "test0rstring", RString "valout"; + "test0rstringlist", RStringList "valout"; + "test0rintbool", RIntBool ("valout", "valout"); + "test0rpvlist", RPVList "valout"; + "test0rvglist", RVGList "valout"; + "test0rlvlist", RLVList "valout"; + "test0rstat", RStat "valout"; + "test0rstatvfs", RStatVFS "valout"; + "test0rhashtable", RHashtable "valout"; +] + +let test_functions = [ + ("test0", (RErr, test_all_args), -1, [NotInFish; NotInDocs], + [], + "internal test function - do not use", + "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +parameter type correctly. + +It echos the contents of each parameter to stdout. + +You probably don't want to call this function."); +] @ List.flatten ( + List.map ( + fun (name, ret) -> + [(name, (ret, [String "val"]), -1, [NotInFish; NotInDocs], + [], + "internal test function - do not use", + "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +return type correctly. + +It converts string C to the return type. + +You probably don't want to call this function."); + (name ^ "err", (ret, []), -1, [NotInFish; NotInDocs], + [], + "internal test function - do not use", + "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +return type correctly. + +This function always returns an error. + +You probably don't want to call this function.")] + ) test_all_rets +) + +(* non_daemon_functions are any functions which don't get processed + * in the daemon, eg. functions for setting and getting local + * configuration values. + *) + +let non_daemon_functions = test_functions @ [ ("launch", (RErr, []), -1, [FishAlias "run"; FishAction "launch"], [], "launch the qemu subprocess", @@ -259,6 +393,29 @@ The first character of C string must be a C<-> (dash). C can be NULL."); + ("set_qemu", (RErr, [String "qemu"]), -1, [FishAlias "qemu"], + [], + "set the qemu binary", + "\ +Set the qemu binary that we will use. + +The default is chosen when the library was compiled by the +configure script. + +You can also override this by setting the C +environment variable. + +Setting C to C restores the default qemu binary."); + + ("get_qemu", (RConstString "qemu", []), -1, [], + [], + "get the qemu binary", + "\ +Return the current qemu binary. + +This is always non-NULL. If it wasn't set already, then this will +return the default qemu binary name."); + ("set_path", (RErr, [String "path"]), -1, [FishAlias "path"], [], "set the search path", @@ -268,9 +425,6 @@ Set the path that libguestfs searches for kernel and initrd.img. The default is C<$libdir/guestfs> unless overridden by setting C environment variable. -The string C is stashed in the libguestfs handle, so the caller -must make sure it remains valid for the lifetime of the handle. - Setting C to C restores the default path."); ("get_path", (RConstString "path", []), -1, [], @@ -282,13 +436,39 @@ 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"], + [], + "add options to kernel command line", + "\ +This function is used to add additional options to the +guest kernel command line. + +The default is C unless overridden by setting +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 the additional kernel options", + "\ +Return the additional kernel options which are added to the +guest kernel command line. + +If C then no options are added."); + ("set_autosync", (RErr, [Bool "autosync"]), -1, [FishAlias "autosync"], [], "set autosync mode", "\ If C is true, this enables autosync. Libguestfs will make a -best effort attempt to run C when the handle is closed -(also if the program exits without closing handles)."); +best effort attempt to run C followed by +C when the handle is closed +(also if the program exits without closing handles). + +This is disabled by default (except in guestfish where it is +enabled by default)."); ("get_autosync", (RBool "autosync", []), -1, [], [], @@ -309,12 +489,90 @@ C is defined and set to C<1>."); [], "get verbose mode", "\ -This returns the verbose messages flag.") +This returns the verbose messages flag."); + + ("is_ready", (RBool "ready", []), -1, [], + [], + "is ready to accept commands", + "\ +This returns true iff this handle is ready to accept commands +(in the C state). + +For more information on states, see L."); + + ("is_config", (RBool "config", []), -1, [], + [], + "is in configuration state", + "\ +This returns true iff this handle is being configured +(in the C state). + +For more information on states, see L."); + + ("is_launching", (RBool "launching", []), -1, [], + [], + "is launching subprocess", + "\ +This returns true iff this handle is launching the subprocess +(in the C state). + +For more information on states, see L."); + + ("is_busy", (RBool "busy", []), -1, [], + [], + "is busy processing a command", + "\ +This returns true iff this handle is busy processing a command +(in the C state). + +For more information on states, see L."); + + ("get_state", (RInt "state", []), -1, [], + [], + "get the current state", + "\ +This returns the current state as an opaque integer. This is +only useful for printing debug and internal error messages. + +For more information on states, see L."); + + ("set_busy", (RErr, []), -1, [NotInFish], + [], + "set state to busy", + "\ +This sets the state to C. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + + ("set_ready", (RErr, []), -1, [NotInFish], + [], + "set state to ready", + "\ +This sets the state to C. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + + ("end_busy", (RErr, []), -1, [NotInFish], + [], + "leave the busy state", + "\ +This sets the state to C, or if in C then it leaves the +state as is. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + ] +(* daemon_functions are any functions which cause some action + * to take place in the daemon. + *) + let daemon_functions = [ ("mount", (RErr, [String "device"; String "mountpoint"]), 1, [], - [InitEmpty, TestOutput ( + [InitEmpty, Always, TestOutput ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; @@ -340,7 +598,7 @@ The filesystem options C and C are set with this call, in order to improve reliability."); ("sync", (RErr, []), 2, [], - [ InitEmpty, TestRun [["sync"]]], + [ InitEmpty, Always, TestRun [["sync"]]], "sync disks, writes are flushed through to the disk image", "\ This syncs the disk, so that any writes are flushed through to the @@ -350,7 +608,7 @@ You should always call this if you have modified a disk image, before closing the handle."); ("touch", (RErr, [String "path"]), 3, [], - [InitBasicFS, TestOutputTrue ( + [InitBasicFS, Always, TestOutputTrue ( [["touch"; "/new"]; ["exists"; "/new"]])], "update file timestamps or create a new file", @@ -360,7 +618,7 @@ 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, TestOutput ( + [InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "new file contents"; "0"]; ["cat"; "/new"]], "new file contents")], "list the contents of a file", @@ -369,7 +627,7 @@ 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 +as end of string). For those you need to use the C function which has a more complex interface."); ("ll", (RString "listing", [String "directory"]), 5, [], @@ -385,7 +643,7 @@ This command is mostly useful for interactive sessions. It is I intended that you try to parse the output string."); ("ls", (RStringList "listing", [String "directory"]), 6, [], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["touch"; "/new"]; ["touch"; "/newer"]; ["touch"; "/newest"]; @@ -400,7 +658,7 @@ This command is mostly useful for interactive sessions. Programs should probably use C instead."); ("list_devices", (RStringList "devices", []), 7, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"])], "list the block devices", "\ @@ -409,9 +667,9 @@ List all the block devices. The full block device names are returned, eg. C"); ("list_partitions", (RStringList "partitions", []), 8, [], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["list_partitions"]], ["/dev/sda1"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])], "list the partitions", @@ -424,9 +682,9 @@ This does not return logical volumes. For that you will need to call C."); ("pvs", (RStringList "physvols", []), 9, [], - [InitBasicFSonLVM, TestOutputList ( + [InitBasicFSonLVM, Always, TestOutputList ( [["pvs"]], ["/dev/sda1"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -443,9 +701,9 @@ PVs (eg. C). See also C."); ("vgs", (RStringList "volgroups", []), 10, [], - [InitBasicFSonLVM, TestOutputList ( + [InitBasicFSonLVM, Always, TestOutputList ( [["vgs"]], ["VG"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -464,9 +722,9 @@ detected (eg. C). See also C."); ("lvs", (RStringList "logvols", []), 11, [], - [InitBasicFSonLVM, TestOutputList ( + [InitBasicFSonLVM, Always, TestOutputList ( [["lvs"]], ["/dev/VG/LV"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -509,10 +767,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, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["write_file"; "/new"; "line1\r\nline2\nline3"; "0"]; ["read_lines"; "/new"]], ["line1"; "line2"; "line3"]); - InitBasicFS, TestOutputList ( + InitBasicFS, Always, TestOutputList ( [["write_file"; "/new"; ""; "0"]; ["read_lines"; "/new"]], [])], "read file as lines", @@ -687,12 +945,12 @@ This is just a shortcut for listing C C and sorting the resulting nodes into alphabetical order."); ("rm", (RErr, [String "path"]), 29, [], - [InitBasicFS, TestRun + [InitBasicFS, Always, TestRun [["touch"; "/new"]; ["rm"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["rm"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["mkdir"; "/new"]; ["rm"; "/new"]]], "remove a file", @@ -700,12 +958,12 @@ C and sorting the resulting nodes into alphabetical order."); Remove the single file C."); ("rmdir", (RErr, [String "path"]), 30, [], - [InitBasicFS, TestRun + [InitBasicFS, Always, TestRun [["mkdir"; "/new"]; ["rmdir"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["rmdir"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["touch"; "/new"]; ["rmdir"; "/new"]]], "remove a directory", @@ -713,7 +971,7 @@ Remove the single file C."); Remove the single directory C."); ("rm_rf", (RErr, [String "path"]), 31, [], - [InitBasicFS, TestOutputFalse + [InitBasicFS, Always, TestOutputFalse [["mkdir"; "/new"]; ["mkdir"; "/new/foo"]; ["touch"; "/new/foo/bar"]; @@ -726,25 +984,32 @@ contents if its a directory. This is like the C shell command."); ("mkdir", (RErr, [String "path"]), 32, [], - [InitBasicFS, TestOutputTrue + [InitBasicFS, Always, TestOutputTrue [["mkdir"; "/new"]; ["is_dir"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["mkdir"; "/new/foo/bar"]]], "create a directory", "\ Create a directory named C."); ("mkdir_p", (RErr, [String "path"]), 33, [], - [InitBasicFS, TestOutputTrue + [InitBasicFS, Always, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; ["is_dir"; "/new/foo/bar"]]; - InitBasicFS, TestOutputTrue + InitBasicFS, Always, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; ["is_dir"; "/new/foo"]]; - InitBasicFS, TestOutputTrue + InitBasicFS, Always, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; - ["is_dir"; "/new"]]], + ["is_dir"; "/new"]]; + (* Regression tests for RHBZ#503133: *) + InitBasicFS, Always, TestRun + [["mkdir"; "/new"]; + ["mkdir_p"; "/new"]]; + InitBasicFS, Always, TestLastFail + [["touch"; "/new"]; + ["mkdir_p"; "/new"]]], "create a directory and parents", "\ Create a directory named C, creating any parent directories @@ -768,10 +1033,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, TestOutputTrue ( + [InitBasicFS, Always, TestOutputTrue ( [["touch"; "/new"]; ["exists"; "/new"]]); - InitBasicFS, TestOutputTrue ( + InitBasicFS, Always, TestOutputTrue ( [["mkdir"; "/new"]; ["exists"; "/new"]])], "test if file or directory exists", @@ -782,10 +1047,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, TestOutputTrue ( + [InitBasicFS, Always, TestOutputTrue ( [["touch"; "/new"]; ["is_file"; "/new"]]); - InitBasicFS, TestOutputFalse ( + InitBasicFS, Always, TestOutputFalse ( [["mkdir"; "/new"]; ["is_file"; "/new"]])], "test if file exists", @@ -797,10 +1062,10 @@ other objects like directories. See also C."); ("is_dir", (RBool "dirflag", [String "path"]), 38, [], - [InitBasicFS, TestOutputFalse ( + [InitBasicFS, Always, TestOutputFalse ( [["touch"; "/new"]; ["is_dir"; "/new"]]); - InitBasicFS, TestOutputTrue ( + InitBasicFS, Always, TestOutputTrue ( [["mkdir"; "/new"]; ["is_dir"; "/new"]])], "test if file exists", @@ -812,7 +1077,7 @@ other objects like files. See also C."); ("pvcreate", (RErr, [String "device"]), 39, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -825,7 +1090,7 @@ where C should usually be a partition name such as C."); ("vgcreate", (RErr, [String "volgroup"; StringList "physvols"]), 40, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -839,7 +1104,7 @@ This creates an LVM volume group called C from the non-empty list of physical volumes C."); ("lvcreate", (RErr, [String "logvol"; String "volgroup"; Int "mbytes"]), 41, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -860,7 +1125,7 @@ This creates an LVM volume group called C on the volume group C, with C megabytes."); ("mkfs", (RErr, [String "fstype"; String "device"]), 42, [], - [InitEmpty, TestOutput ( + [InitEmpty, Always, TestOutput ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; @@ -869,7 +1134,7 @@ on the volume group C, with C megabytes."); "make a filesystem", "\ This creates a filesystem on C (usually a partition -of LVM logical volume). The filesystem type is C, for +or LVM logical volume). The filesystem type is C, for example C."); ("sfdisk", (RErr, [String "device"; @@ -896,15 +1161,29 @@ information refer to the L manpage. To create a single partition occupying the whole disk, you would pass C as a single element list, when the single element being -the string C<,> (comma)."); +the string C<,> (comma). + +See also: C, C"); ("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning], - [InitEmpty, TestOutput ( - [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; - ["mkfs"; "ext2"; "/dev/sda1"]; - ["mount"; "/dev/sda1"; "/"]; - ["write_file"; "/new"; "new file contents"; "0"]; - ["cat"; "/new"]], "new file contents")], + [InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "new file contents"; "0"]; + ["cat"; "/new"]], "new file contents"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "\nnew file contents\n"; "0"]; + ["cat"; "/new"]], "\nnew file contents\n"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "\n\n"; "0"]; + ["cat"; "/new"]], "\n\n"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; ""; "0"]; + ["cat"; "/new"]], ""); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "\n\n\n"; "0"]; + ["cat"; "/new"]], "\n\n\n"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "\n"; "0"]; + ["cat"; "/new"]], "\n")], "create a file", "\ This call creates a file called C. The contents of the @@ -913,15 +1192,20 @@ with length C. As a special case, if C is C<0> then the length is calculated using C (so in this case -the content cannot contain embedded ASCII NULs)."); +the content cannot contain embedded ASCII NULs). + +I Owing to a bug, writing content containing ASCII NUL +characters does I work, even if the length is specified. +We hope to resolve this bug in a future version. In the meantime +use C."); ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["mounts"]], ["/dev/sda1"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; @@ -934,7 +1218,7 @@ specified either by its mountpoint (path) or the device which contains the filesystem."); ("mounts", (RStringList "devices", []), 46, [], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["mounts"]], ["/dev/sda1"])], "show mounted filesystems", "\ @@ -944,8 +1228,22 @@ the list of devices (eg. C, C). Some internal mounts are not shown."); ("umount_all", (RErr, []), 47, [FishAlias "unmount-all"], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["umount_all"]; + ["mounts"]], []); + (* check that umount_all can unmount nested mounts correctly: *) + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; + ["mkfs"; "ext2"; "/dev/sda1"]; + ["mkfs"; "ext2"; "/dev/sda2"]; + ["mkfs"; "ext2"; "/dev/sda3"]; + ["mount"; "/dev/sda1"; "/"]; + ["mkdir"; "/mp1"]; + ["mount"; "/dev/sda2"; "/mp1"]; + ["mkdir"; "/mp1/mp2"]; + ["mount"; "/dev/sda3"; "/mp1/mp2"]; + ["mkdir"; "/mp1/mp2/mp3"]; + ["umount_all"]; ["mounts"]], [])], "unmount all filesystems", "\ @@ -961,13 +1259,13 @@ This command removes all LVM logical volumes, volume groups and physical volumes."); ("file", (RString "description", [String "path"]), 49, [], - [InitBasicFS, TestOutput ( + [InitBasicFS, Always, TestOutput ( [["touch"; "/new"]; ["file"; "/new"]], "empty"); - InitBasicFS, TestOutput ( + InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "some content\n"; "0"]; ["file"; "/new"]], "ASCII text"); - InitBasicFS, TestLastFail ( + InitBasicFS, Always, TestLastFail ( [["file"; "/nofile"]])], "determine file type", "\ @@ -979,8 +1277,55 @@ The exact command which runs is C. Note in particular that the filename is not prepended to the output (the C<-b> option)."); - ("command", (RString "output", [StringList "arguments"]), 50, [], - [], (* XXX how to test? *) + ("command", (RString "output", [StringList "arguments"]), 50, [ProtocolLimitWarning], + [InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 1"]], "Result1"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 2"]], "Result2\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 3"]], "\nResult3"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 4"]], "\nResult4\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 5"]], "\nResult5\n\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 6"]], "\n\nResult6\n\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 7"]], ""); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 8"]], "\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 9"]], "\n\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 10"]], "Result10-1\nResult10-2\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 11"]], "Result11-1\nResult11-2"); + InitBasicFS, Always, TestLastFail ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command"]])], "run a command from the guest filesystem", "\ This call runs a command from the guest filesystem. The @@ -993,6 +1338,13 @@ The first element is the name of the program to run. Subsequent elements are parameters. The list must be non-empty (ie. must contain a program name). +The return value is anything printed to I by +the command. + +If the command returns a non-zero exit status, then +this function returns an error message. The error message +string is the content of I from the command. + The C<$PATH> environment variable will contain at least C and C. If you require a program from another location, you should provide the full path in the @@ -1004,15 +1356,58 @@ correct places. It is the caller's responsibility to ensure all filesystems that are needed are mounted at the right locations."); - ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [], - [], (* XXX how to test? *) + ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [ProtocolLimitWarning], + [InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 1"]], ["Result1"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 2"]], ["Result2"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 3"]], ["";"Result3"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 4"]], ["";"Result4"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 5"]], ["";"Result5";""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 6"]], ["";"";"Result6";""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 7"]], []); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 8"]], [""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 9"]], ["";""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 10"]], ["Result10-1";"Result10-2"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 11"]], ["Result11-1";"Result11-2"])], "run a command, returning lines", "\ This is the same as C, but splits the result into a list of lines."); ("stat", (RStat "statbuf", [String "path"]), 52, [], - [InitBasicFS, TestOutputStruct ( + [InitBasicFS, Always, TestOutputStruct ( [["touch"; "/new"]; ["stat"; "/new"]], [CompareWithInt ("size", 0)])], "get file information", @@ -1022,7 +1417,7 @@ Returns file information for the given C. This is the same as the C system call."); ("lstat", (RStat "statbuf", [String "path"]), 53, [], - [InitBasicFS, TestOutputStruct ( + [InitBasicFS, Always, TestOutputStruct ( [["touch"; "/new"]; ["lstat"; "/new"]], [CompareWithInt ("size", 0)])], "get file information for a symbolic link", @@ -1036,7 +1431,7 @@ refers to. This is the same as the C system call."); ("statvfs", (RStatVFS "statbuf", [String "path"]), 54, [], - [InitBasicFS, TestOutputStruct ( + [InitBasicFS, Always, TestOutputStruct ( [["statvfs"; "/"]], [CompareWithInt ("bfree", 487702); CompareWithInt ("blocks", 490020); CompareWithInt ("bsize", 1024)])], @@ -1048,2867 +1443,4711 @@ C should be a file or directory in the mounted file system This is the same as the C system call."); -] + ("tune2fs_l", (RHashtable "superblock", [String "device"]), 55, [], + [], (* XXX test *) + "get ext2/ext3/ext4 superblock details", + "\ +This returns the contents of the ext2, ext3 or ext4 filesystem +superblock on C. + +It is the same as running C. See L +manpage for more details. The list of fields returned isn't +clearly defined, and depends on both the version of C +that libguestfs was built against, and the filesystem itself."); + + ("blockdev_setro", (RErr, [String "device"]), 56, [], + [InitEmpty, Always, TestOutputTrue ( + [["blockdev_setro"; "/dev/sda"]; + ["blockdev_getro"; "/dev/sda"]])], + "set block device to read-only", + "\ +Sets the block device named C to read-only. -let all_functions = non_daemon_functions @ daemon_functions +This uses the L command."); -(* In some places we want the functions to be displayed sorted - * alphabetically, so this is useful: - *) -let all_functions_sorted = - List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) -> - compare n1 n2) all_functions + ("blockdev_setrw", (RErr, [String "device"]), 57, [], + [InitEmpty, Always, TestOutputFalse ( + [["blockdev_setrw"; "/dev/sda"]; + ["blockdev_getro"; "/dev/sda"]])], + "set block device to read-write", + "\ +Sets the block device named C to read-write. -(* Column names and types from LVM PVs/VGs/LVs. *) -let pv_cols = [ - "pv_name", `String; - "pv_uuid", `UUID; - "pv_fmt", `String; - "pv_size", `Bytes; - "dev_size", `Bytes; - "pv_free", `Bytes; - "pv_used", `Bytes; - "pv_attr", `String (* XXX *); - "pv_pe_count", `Int; - "pv_pe_alloc_count", `Int; - "pv_tags", `String; - "pe_start", `Bytes; - "pv_mda_count", `Int; - "pv_mda_free", `Bytes; -(* Not in Fedora 10: - "pv_mda_size", `Bytes; -*) -] -let vg_cols = [ - "vg_name", `String; - "vg_uuid", `UUID; - "vg_fmt", `String; - "vg_attr", `String (* XXX *); - "vg_size", `Bytes; - "vg_free", `Bytes; - "vg_sysid", `String; - "vg_extent_size", `Bytes; - "vg_extent_count", `Int; - "vg_free_count", `Int; - "max_lv", `Int; - "max_pv", `Int; - "pv_count", `Int; - "lv_count", `Int; - "snap_count", `Int; - "vg_seqno", `Int; - "vg_tags", `String; - "vg_mda_count", `Int; - "vg_mda_free", `Bytes; -(* Not in Fedora 10: - "vg_mda_size", `Bytes; -*) -] -let lv_cols = [ - "lv_name", `String; - "lv_uuid", `UUID; - "lv_attr", `String (* XXX *); - "lv_major", `Int; - "lv_minor", `Int; - "lv_kernel_major", `Int; - "lv_kernel_minor", `Int; - "lv_size", `Bytes; - "seg_count", `Int; - "origin", `String; - "snap_percent", `OptPercent; - "copy_percent", `OptPercent; - "move_pv", `String; - "lv_tags", `String; - "mirror_log", `String; - "modules", `String; -] +This uses the L command."); -(* Column names and types from stat structures. - * NB. Can't use things like 'st_atime' because glibc header files - * define some of these as macros. Ugh. - *) -let stat_cols = [ - "dev", `Int; - "ino", `Int; - "mode", `Int; - "nlink", `Int; - "uid", `Int; - "gid", `Int; - "rdev", `Int; - "size", `Int; - "blksize", `Int; - "blocks", `Int; - "atime", `Int; - "mtime", `Int; - "ctime", `Int; -] -let statvfs_cols = [ - "bsize", `Int; - "frsize", `Int; - "blocks", `Int; - "bfree", `Int; - "bavail", `Int; - "files", `Int; - "ffree", `Int; - "favail", `Int; - "fsid", `Int; - "flag", `Int; - "namemax", `Int; -] + ("blockdev_getro", (RBool "ro", [String "device"]), 58, [], + [InitEmpty, Always, TestOutputTrue ( + [["blockdev_setro"; "/dev/sda"]; + ["blockdev_getro"; "/dev/sda"]])], + "is block device set to read-only", + "\ +Returns a boolean indicating if the block device is read-only +(true if read-only, false if not). -(* Useful functions. - * Note we don't want to use any external OCaml libraries which - * makes this a bit harder than it should be. - *) -let failwithf fs = ksprintf failwith fs +This uses the L command."); -let replace_char s c1 c2 = - let s2 = String.copy s in - let r = ref false in - for i = 0 to String.length s2 - 1 do - if String.unsafe_get s2 i = c1 then ( - String.unsafe_set s2 i c2; - r := true - ) - done; - if not !r then s else s2 + ("blockdev_getss", (RInt "sectorsize", [String "device"]), 59, [], + [InitEmpty, Always, TestOutputInt ( + [["blockdev_getss"; "/dev/sda"]], 512)], + "get sectorsize of block device", + "\ +This returns the size of sectors on a block device. +Usually 512, but can be larger for modern devices. -let rec find s sub = - let len = String.length s in - let sublen = String.length sub in - let rec loop i = - if i <= len-sublen then ( - let rec loop2 j = - if j < sublen then ( - if s.[i+j] = sub.[j] then loop2 (j+1) - else -1 - ) else - i (* found *) - in - let r = loop2 0 in - if r = -1 then loop (i+1) else r - ) else - -1 (* not found *) - in - loop 0 +(Note, this is not the size in sectors, use C +for that). -let rec replace_str s s1 s2 = - let len = String.length s in - let sublen = String.length s1 in - let i = find s s1 in - if i = -1 then s - else ( - let s' = String.sub s 0 i in - let s'' = String.sub s (i+sublen) (len-i-sublen) in - s' ^ s2 ^ replace_str s'' s1 s2 - ) +This uses the L command."); -let rec string_split sep str = - let len = String.length str in - let seplen = String.length sep in - let i = find str sep in - if i = -1 then [str] - else ( - let s' = String.sub str 0 i in - let s'' = String.sub str (i+seplen) (len-i-seplen) in - s' :: string_split sep s'' - ) + ("blockdev_getbsz", (RInt "blocksize", [String "device"]), 60, [], + [InitEmpty, Always, TestOutputInt ( + [["blockdev_getbsz"; "/dev/sda"]], 4096)], + "get blocksize of block device", + "\ +This returns the block size of a device. -let rec find_map f = function - | [] -> raise Not_found - | x :: xs -> - match f x with - | Some y -> y - | None -> find_map f xs +(Note this is different from both I and +I). -let iteri f xs = - let rec loop i = function - | [] -> () - | x :: xs -> f i x; loop (i+1) xs - in - loop 0 xs +This uses the L command."); -let mapi f xs = - let rec loop i = function - | [] -> [] - | x :: xs -> let r = f i x in r :: loop (i+1) xs - in - loop 0 xs + ("blockdev_setbsz", (RErr, [String "device"; Int "blocksize"]), 61, [], + [], (* XXX test *) + "set blocksize of block device", + "\ +This sets the block size of a device. -let name_of_argt = function - | String n | OptString n | StringList n | Bool n | Int n -> n +(Note this is different from both I and +I). -(* Check function names etc. for consistency. *) -let check_functions () = - let contains_uppercase str = - let len = String.length str in - let rec loop i = - if i >= len then false - else ( - let c = str.[i] in - if c >= 'A' && c <= 'Z' then true - else loop (i+1) - ) - in - loop 0 - in +This uses the L command."); - (* Check function names. *) - List.iter ( - fun (name, _, _, _, _, _, _) -> - if String.length name >= 7 && String.sub name 0 7 = "guestfs" then - failwithf "function name %s does not need 'guestfs' prefix" name; - if contains_uppercase name then - failwithf "function name %s should not contain uppercase chars" name; - if String.contains name '-' then - failwithf "function name %s should not contain '-', use '_' instead." - name - ) all_functions; + ("blockdev_getsz", (RInt64 "sizeinsectors", [String "device"]), 62, [], + [InitEmpty, Always, TestOutputInt ( + [["blockdev_getsz"; "/dev/sda"]], 1024000)], + "get total size of device in 512-byte sectors", + "\ +This returns the size of the device in units of 512-byte sectors +(even if the sectorsize isn't 512 bytes ... weird). - (* Check function parameter/return names. *) - List.iter ( - fun (name, style, _, _, _, _, _) -> - let check_arg_ret_name n = - if contains_uppercase n then - failwithf "%s param/ret %s should not contain uppercase chars" - name n; - if String.contains n '-' || String.contains n '_' then - failwithf "%s param/ret %s should not contain '-' or '_'" - name n; - if n = "value" then - failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n; - if n = "argv" || n = "args" then - failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n - in +See also C for the real sector size of +the device, and C for the more +useful I. - (match fst style with - | RErr -> () - | RInt n | RBool n | RConstString n | RString n - | RStringList n | RPVList n | RVGList n | RLVList n - | RStat n | RStatVFS n -> - check_arg_ret_name n - | RIntBool (n,m) -> - check_arg_ret_name n; - check_arg_ret_name m - ); - List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) (snd style) - ) all_functions; +This uses the L command."); - (* Check short descriptions. *) - List.iter ( - fun (name, _, _, _, _, shortdesc, _) -> - if shortdesc.[0] <> Char.lowercase shortdesc.[0] then - failwithf "short description of %s should begin with lowercase." name; - let c = shortdesc.[String.length shortdesc-1] in - if c = '\n' || c = '.' then - failwithf "short description of %s should not end with . or \\n." name - ) all_functions; + ("blockdev_getsize64", (RInt64 "sizeinbytes", [String "device"]), 63, [], + [InitEmpty, Always, TestOutputInt ( + [["blockdev_getsize64"; "/dev/sda"]], 524288000)], + "get total size of device in bytes", + "\ +This returns the size of the device in bytes. - (* Check long dscriptions. *) - List.iter ( - fun (name, _, _, _, _, _, longdesc) -> - if longdesc.[String.length longdesc-1] = '\n' then - failwithf "long description of %s should not end with \\n." name - ) all_functions; +See also C. - (* Check proc_nrs. *) - List.iter ( - fun (name, _, proc_nr, _, _, _, _) -> - if proc_nr <= 0 then - failwithf "daemon function %s should have proc_nr > 0" name - ) daemon_functions; +This uses the L command."); - List.iter ( - fun (name, _, proc_nr, _, _, _, _) -> - if proc_nr <> -1 then - failwithf "non-daemon function %s should have proc_nr -1" name - ) non_daemon_functions; + ("blockdev_flushbufs", (RErr, [String "device"]), 64, [], + [InitEmpty, Always, TestRun + [["blockdev_flushbufs"; "/dev/sda"]]], + "flush device buffers", + "\ +This tells the kernel to flush internal buffers associated +with C. - let proc_nrs = - List.map (fun (name, _, proc_nr, _, _, _, _) -> name, proc_nr) - daemon_functions in - let proc_nrs = - List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in - let rec loop = function - | [] -> () - | [_] -> () - | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 -> - loop rest - | (name1,nr1) :: (name2,nr2) :: _ -> - failwithf "%s and %s have conflicting procedure numbers (%d, %d)" - name1 name2 nr1 nr2 - in - loop proc_nrs +This uses the L command."); -(* 'pr' prints to the current output file. *) -let chan = ref stdout -let pr fs = ksprintf (output_string !chan) fs + ("blockdev_rereadpt", (RErr, [String "device"]), 65, [], + [InitEmpty, Always, TestRun + [["blockdev_rereadpt"; "/dev/sda"]]], + "reread partition table", + "\ +Reread the partition table on C. -(* Generate a header block in a number of standard styles. *) -type comment_style = CStyle | HashStyle | OCamlStyle -type license = GPLv2 | LGPLv2 +This uses the L command."); -let generate_header comment license = - let c = match comment with - | CStyle -> pr "/* "; " *" - | HashStyle -> pr "# "; "#" - | OCamlStyle -> pr "(* "; " *" in - pr "libguestfs generated file\n"; - pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c; - pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c; - pr "%s\n" c; - pr "%s Copyright (C) 2009 Red Hat Inc.\n" c; - pr "%s\n" c; - (match license with - | GPLv2 -> - pr "%s This program is free software; you can redistribute it and/or modify\n" c; - pr "%s it under the terms of the GNU General Public License as published by\n" c; - pr "%s the Free Software Foundation; either version 2 of the License, or\n" c; - pr "%s (at your option) any later version.\n" c; - pr "%s\n" c; - pr "%s This program is distributed in the hope that it will be useful,\n" c; - pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c; - pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" c; - pr "%s GNU General Public License for more details.\n" c; - pr "%s\n" c; - pr "%s You should have received a copy of the GNU General Public License along\n" c; - pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c; - pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c; + ("upload", (RErr, [FileIn "filename"; String "remotefilename"]), 66, [], + [InitBasicFS, Always, TestOutput ( + (* Pick a file from cwd which isn't likely to change. *) + [["upload"; "../COPYING.LIB"; "/COPYING.LIB"]; + ["checksum"; "md5"; "/COPYING.LIB"]], "e3eda01d9815f8d24aae2dbd89b68b06")], + "upload a file from the local machine", + "\ +Upload local file C to C on the +filesystem. - | LGPLv2 -> - pr "%s This library is free software; you can redistribute it and/or\n" c; - pr "%s modify it under the terms of the GNU Lesser General Public\n" c; - pr "%s License as published by the Free Software Foundation; either\n" c; - pr "%s version 2 of the License, or (at your option) any later version.\n" c; - pr "%s\n" c; - pr "%s This library is distributed in the hope that it will be useful,\n" c; - pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c; - pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" c; - pr "%s Lesser General Public License for more details.\n" c; - pr "%s\n" c; - pr "%s You should have received a copy of the GNU Lesser General Public\n" c; - pr "%s License along with this library; if not, write to the Free Software\n" c; - pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c; - ); - (match comment with - | CStyle -> pr " */\n" - | HashStyle -> () - | OCamlStyle -> pr " *)\n" - ); - pr "\n" +C can also be a named pipe. -(* Start of main code generation functions below this line. *) +See also C."); -(* Generate the pod documentation for the C API. *) -let rec generate_actions_pod () = - List.iter ( - fun (shortname, style, _, flags, _, _, longdesc) -> - let name = "guestfs_" ^ shortname in - pr "=head2 %s\n\n" name; - pr " "; - generate_prototype ~extern:false ~handle:"handle" name style; - pr "\n\n"; - pr "%s\n\n" longdesc; - (match fst style with - | RErr -> - pr "This function returns 0 on success or -1 on error.\n\n" - | RInt _ -> - pr "On error this function returns -1.\n\n" - | RBool _ -> - pr "This function returns a C truth value on success or -1 on error.\n\n" - | 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" - | RString _ -> - pr "This function returns a string, or NULL on error. -I.\n\n" - | RStringList _ -> - pr "This function returns a NULL-terminated array of strings -(like L), or NULL if there was an error. -I.\n\n" - | RIntBool _ -> - pr "This function returns a C, -or NULL if there was an error. -I after use>.\n\n" - | RPVList _ -> - pr "This function returns a C -(see Eguestfs-structs.hE), -or NULL if there was an error. -I after use>.\n\n" - | RVGList _ -> - pr "This function returns a C -(see Eguestfs-structs.hE), -or NULL if there was an error. -I after use>.\n\n" - | RLVList _ -> - pr "This function returns a C -(see Eguestfs-structs.hE), -or NULL if there was an error. -I after use>.\n\n" - | RStat _ -> - pr "This function returns a C -(see L and Eguestfs-structs.hE), -or NULL if there was an error. -I after use>.\n\n" - | RStatVFS _ -> - pr "This function returns a C -(see L and Eguestfs-structs.hE), -or NULL if there was an error. -I after use>.\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; - ) all_functions_sorted + ("download", (RErr, [String "remotefilename"; FileOut "filename"]), 67, [], + [InitBasicFS, Always, TestOutput ( + (* Pick a file from cwd which isn't likely to change. *) + [["upload"; "../COPYING.LIB"; "/COPYING.LIB"]; + ["download"; "/COPYING.LIB"; "testdownload.tmp"]; + ["upload"; "testdownload.tmp"; "/upload"]; + ["checksum"; "md5"; "/upload"]], "e3eda01d9815f8d24aae2dbd89b68b06")], + "download a file to the local machine", + "\ +Download file C and save it as C +on the local machine. + +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")], + "compute MD5, SHAx or CRC checksum of file", + "\ +This call computes the MD5, SHAx or CRC checksum of the +file named C. -and generate_structs_pod () = - (* LVM structs documentation. *) - List.iter ( - fun (typ, cols) -> - pr "=head2 guestfs_lvm_%s\n" typ; - pr "\n"; - pr " struct guestfs_lvm_%s {\n" typ; - List.iter ( - function - | name, `String -> pr " char *%s;\n" name - | name, `UUID -> - pr " /* The next field is NOT nul-terminated, be careful when printing it: */\n"; - pr " char %s[32];\n" name - | name, `Bytes -> pr " uint64_t %s;\n" name - | name, `Int -> pr " int64_t %s;\n" name - | name, `OptPercent -> - pr " /* The next field is [0..100] or -1 meaning 'not present': */\n"; - pr " float %s;\n" name - ) cols; - pr " \n"; - pr " struct guestfs_lvm_%s_list {\n" typ; - pr " uint32_t len; /* Number of elements in list. */\n"; - pr " struct guestfs_lvm_%s *val; /* Elements. */\n" typ; - pr " };\n"; - pr " \n"; - pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n" - typ typ; - pr "\n" - ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols] +The type of checksum to compute is given by the C +parameter which must have one of the following values: -(* Generate the protocol (XDR) file, 'guestfs_protocol.x' and - * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'. - * - * We have to use an underscore instead of a dash because otherwise - * rpcgen generates incorrect code. - * - * This header is NOT exported to clients, but see also generate_structs_h. - *) -and generate_xdr () = - generate_header CStyle LGPLv2; +=over 4 - (* This has to be defined to get around a limitation in Sun's rpcgen. *) - pr "typedef string str<>;\n"; - pr "\n"; +=item C - (* LVM internal structures. *) - List.iter ( - function - | typ, cols -> - pr "struct guestfs_lvm_int_%s {\n" typ; - List.iter (function - | name, `String -> pr " string %s<>;\n" name - | name, `UUID -> pr " opaque %s[32];\n" name - | name, `Bytes -> pr " hyper %s;\n" name - | name, `Int -> pr " hyper %s;\n" name - | name, `OptPercent -> pr " float %s;\n" name - ) cols; - pr "};\n"; - pr "\n"; - pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ; - pr "\n"; - ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; +Compute the cyclic redundancy check (CRC) specified by POSIX +for the C command. - (* Stat internal structures. *) - List.iter ( - function - | typ, cols -> - pr "struct guestfs_int_%s {\n" typ; - List.iter (function - | name, `Int -> pr " hyper %s;\n" name - ) cols; - pr "};\n"; - pr "\n"; - ) ["stat", stat_cols; "statvfs", statvfs_cols]; +=item C - List.iter ( - fun (shortname, style, _, _, _, _, _) -> - let name = "guestfs_" ^ shortname in +Compute the MD5 hash (using the C program). - (match snd style with - | [] -> () - | args -> - pr "struct %s_args {\n" name; - List.iter ( - function - | String n -> pr " string %s<>;\n" n - | OptString n -> pr " str *%s;\n" n - | StringList n -> pr " str %s<>;\n" n - | Bool n -> pr " bool %s;\n" n - | Int n -> pr " int %s;\n" n - ) args; - pr "};\n\n" - ); - (match fst style with - | RErr -> () - | RInt n -> - pr "struct %s_ret {\n" name; - pr " int %s;\n" n; - pr "};\n\n" - | RBool n -> - pr "struct %s_ret {\n" name; - pr " bool %s;\n" n; - pr "};\n\n" - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" - | RString n -> - pr "struct %s_ret {\n" name; - pr " string %s<>;\n" n; - pr "};\n\n" - | RStringList n -> - pr "struct %s_ret {\n" name; - pr " str %s<>;\n" n; - pr "};\n\n" - | RIntBool (n,m) -> - pr "struct %s_ret {\n" name; - pr " int %s;\n" n; - pr " bool %s;\n" m; - pr "};\n\n" - | RPVList n -> - pr "struct %s_ret {\n" name; - pr " guestfs_lvm_int_pv_list %s;\n" n; - pr "};\n\n" - | RVGList n -> - pr "struct %s_ret {\n" name; - pr " guestfs_lvm_int_vg_list %s;\n" n; - pr "};\n\n" - | RLVList n -> - pr "struct %s_ret {\n" name; - pr " guestfs_lvm_int_lv_list %s;\n" n; - pr "};\n\n" - | RStat n -> - pr "struct %s_ret {\n" name; - pr " guestfs_int_stat %s;\n" n; - pr "};\n\n"; - | RStatVFS n -> - pr "struct %s_ret {\n" name; - pr " guestfs_int_statvfs %s;\n" n; - pr "};\n\n"; - ); - ) daemon_functions; +=item C - (* Table of procedure numbers. *) - pr "enum guestfs_procedure {\n"; - List.iter ( - fun (shortname, _, proc_nr, _, _, _, _) -> - pr " GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr - ) daemon_functions; - pr " GUESTFS_PROC_dummy\n"; (* so we don't have a "hanging comma" *) - pr "};\n"; - pr "\n"; +Compute the SHA1 hash (using the C program). - (* Having to choose a maximum message size is annoying for several - * reasons (it limits what we can do in the API), but it (a) makes - * the protocol a lot simpler, and (b) provides a bound on the size - * of the daemon which operates in limited memory space. For large - * file transfers you should use FTP. - *) - pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024); - pr "\n"; +=item C - (* Message header, etc. *) - pr "\ -const GUESTFS_PROGRAM = 0x2000F5F5; -const GUESTFS_PROTOCOL_VERSION = 1; +Compute the SHA224 hash (using the C program). -enum guestfs_message_direction { - GUESTFS_DIRECTION_CALL = 0, /* client -> daemon */ - GUESTFS_DIRECTION_REPLY = 1 /* daemon -> client */ -}; +=item C -enum guestfs_message_status { - GUESTFS_STATUS_OK = 0, - GUESTFS_STATUS_ERROR = 1 -}; +Compute the SHA256 hash (using the C program). -const GUESTFS_ERROR_LEN = 256; +=item C -struct guestfs_message_error { - string error; /* error message */ -}; +Compute the SHA384 hash (using the C program). -struct guestfs_message_header { - unsigned prog; /* GUESTFS_PROGRAM */ - unsigned vers; /* GUESTFS_PROTOCOL_VERSION */ - guestfs_procedure proc; /* GUESTFS_PROC_x */ - guestfs_message_direction direction; - unsigned serial; /* message serial number */ - guestfs_message_status status; -}; -" +=item C -(* Generate the guestfs-structs.h file. *) -and generate_structs_h () = - generate_header CStyle LGPLv2; +Compute the SHA512 hash (using the C program). - (* This is a public exported header file containing various - * structures. The structures are carefully written to have - * exactly the same in-memory format as the XDR structures that - * we use on the wire to the daemon. The reason for creating - * copies of these structures here is just so we don't have to - * export the whole of guestfs_protocol.h (which includes much - * unrelated and XDR-dependent stuff that we don't want to be - * public, or required by clients). - * - * To reiterate, we will pass these structures to and from the - * client with a simple assignment or memcpy, so the format - * must be identical to what rpcgen / the RFC defines. - *) +=back - (* guestfs_int_bool structure. *) - pr "struct guestfs_int_bool {\n"; - pr " int32_t i;\n"; - pr " int32_t b;\n"; - pr "};\n"; - pr "\n"; +The checksum is returned as a printable string."); - (* LVM public structures. *) - List.iter ( - function - | typ, cols -> - pr "struct guestfs_lvm_%s {\n" typ; - List.iter ( - function - | name, `String -> pr " char *%s;\n" name - | name, `UUID -> pr " char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name - | name, `Bytes -> pr " uint64_t %s;\n" name - | name, `Int -> pr " int64_t %s;\n" name - | name, `OptPercent -> pr " float %s; /* [0..100] or -1 */\n" name - ) cols; - pr "};\n"; - pr "\n"; - pr "struct guestfs_lvm_%s_list {\n" typ; - pr " uint32_t len;\n"; - pr " struct guestfs_lvm_%s *val;\n" typ; - pr "};\n"; - pr "\n" - ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; + ("tar_in", (RErr, [FileIn "tarfile"; String "directory"]), 69, [], + [InitBasicFS, Always, TestOutput ( + [["tar_in"; "../images/helloworld.tar"; "/"]; + ["cat"; "/hello"]], "hello\n")], + "unpack tarfile to directory", + "\ +This command uploads and unpacks local file C (an +I tar file) into C. - (* Stat structures. *) - List.iter ( - function - | typ, cols -> - pr "struct guestfs_%s {\n" typ; - List.iter ( - function - | name, `Int -> pr " int64_t %s;\n" name - ) cols; - pr "};\n"; - pr "\n" - ) ["stat", stat_cols; "statvfs", statvfs_cols] +To upload a compressed tarball, use C."); -(* Generate the guestfs-actions.h file. *) -and generate_actions_h () = - generate_header CStyle LGPLv2; - List.iter ( - fun (shortname, style, _, _, _, _, _) -> - let name = "guestfs_" ^ shortname in - generate_prototype ~single_line:true ~newline:true ~handle:"handle" - name style - ) all_functions + ("tar_out", (RErr, [String "directory"; FileOut "tarfile"]), 70, [], + [], + "pack directory into tarfile", + "\ +This command packs the contents of C and downloads +it to local file C. -(* Generate the client-side dispatch stubs. *) -and generate_client_actions () = - generate_header CStyle LGPLv2; +To download a compressed tarball, use C."); - (* Client-side stubs for each function. *) - List.iter ( - fun (shortname, style, _, _, _, _, _) -> - let name = "guestfs_" ^ shortname in + ("tgz_in", (RErr, [FileIn "tarball"; String "directory"]), 71, [], + [InitBasicFS, Always, TestOutput ( + [["tgz_in"; "../images/helloworld.tar.gz"; "/"]; + ["cat"; "/hello"]], "hello\n")], + "unpack compressed tarball to directory", + "\ +This command uploads and unpacks local file C (a +I tar file) into C. - (* Generate the return value struct. *) - pr "struct %s_rv {\n" shortname; - pr " int cb_done; /* flag to indicate callback was called */\n"; - pr " struct guestfs_message_header hdr;\n"; - pr " struct guestfs_message_error err;\n"; - (match fst style with - | RErr -> () - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" - | RInt _ - | RBool _ | RString _ | RStringList _ - | RIntBool _ - | RPVList _ | RVGList _ | RLVList _ - | RStat _ | RStatVFS _ -> - pr " struct %s_ret ret;\n" name - ); - pr "};\n\n"; +To upload an uncompressed tarball, use C."); - (* Generate the callback function. *) - pr "static void %s_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname; - pr "{\n"; - pr " struct %s_rv *rv = (struct %s_rv *) data;\n" shortname shortname; - pr "\n"; - pr " if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {\n"; - pr " error (g, \"%s: failed to parse reply header\");\n" name; - pr " return;\n"; - pr " }\n"; - pr " if (rv->hdr.status == GUESTFS_STATUS_ERROR) {\n"; - pr " if (!xdr_guestfs_message_error (xdr, &rv->err)) {\n"; - pr " error (g, \"%s: failed to parse reply error\");\n" name; - pr " return;\n"; - pr " }\n"; - pr " goto done;\n"; - pr " }\n"; + ("tgz_out", (RErr, [String "directory"; FileOut "tarball"]), 72, [], + [], + "pack directory into compressed tarball", + "\ +This command packs the contents of C and downloads +it to local file C. + +To download an uncompressed tarball, use C."); + + ("mount_ro", (RErr, [String "device"; String "mountpoint"]), 73, [], + [InitBasicFS, Always, TestLastFail ( + [["umount"; "/"]; + ["mount_ro"; "/dev/sda1"; "/"]; + ["touch"; "/new"]]); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "data"; "0"]; + ["umount"; "/"]; + ["mount_ro"; "/dev/sda1"; "/"]; + ["cat"; "/new"]], "data")], + "mount a guest disk, read-only", + "\ +This is the same as the C command, but it +mounts the filesystem with the read-only (I<-o ro>) flag."); - (match fst style with - | RErr -> () - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" - | RInt _ - | RBool _ | RString _ | RStringList _ - | RIntBool _ - | RPVList _ | RVGList _ | RLVList _ - | RStat _ | RStatVFS _ -> - pr " if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name; - pr " error (g, \"%s: failed to parse reply\");\n" name; - pr " return;\n"; - pr " }\n"; - ); + ("mount_options", (RErr, [String "options"; String "device"; String "mountpoint"]), 74, [], + [], + "mount a guest disk with mount options", + "\ +This is the same as the C command, but it +allows you to set the mount options as for the +L I<-o> flag."); - pr " done:\n"; - pr " rv->cb_done = 1;\n"; - pr " main_loop.main_loop_quit (g);\n"; - pr "}\n\n"; + ("mount_vfs", (RErr, [String "options"; String "vfstype"; String "device"; String "mountpoint"]), 75, [], + [], + "mount a guest disk with mount options and vfstype", + "\ +This is the same as the C command, but it +allows you to set both the mount options and the vfstype +as for the L I<-o> and I<-t> flags."); - (* Generate the action stub. *) - generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" name style; + ("debug", (RString "result", [String "subcmd"; StringList "extraargs"]), 76, [], + [], + "debugging and internals", + "\ +The C command exposes some internals of +C (the guestfs daemon) that runs inside the +qemu subprocess. - let error_code = - match fst style with - | RErr | RInt _ | RBool _ -> "-1" - | RConstString _ -> - failwithf "RConstString cannot be returned from a daemon function" - | RString _ | RStringList _ | RIntBool _ - | RPVList _ | RVGList _ | RLVList _ - | RStat _ | RStatVFS _ -> - "NULL" in +There is no comprehensive help for this command. You have +to look at the file C in the libguestfs source +to find out what you can do."); - pr "{\n"; + ("lvremove", (RErr, [String "device"]), 77, [], + [InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["lvremove"; "/dev/VG/LV1"]; + ["lvs"]], ["/dev/VG/LV2"]); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["lvremove"; "/dev/VG"]; + ["lvs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["lvremove"; "/dev/VG"]; + ["vgs"]], ["VG"])], + "remove an LVM logical volume", + "\ +Remove an LVM logical volume C, where C is +the path to the LV, such as C. - (match snd style with - | [] -> () - | _ -> pr " struct %s_args args;\n" name - ); +You can also remove all LVs in a volume group by specifying +the VG name, C."); - pr " struct %s_rv rv;\n" shortname; - pr " int serial;\n"; - pr "\n"; - pr " if (g->state != READY) {\n"; - pr " error (g, \"%s called from the wrong state, %%d != READY\",\n" - name; - pr " g->state);\n"; - pr " return %s;\n" error_code; - pr " }\n"; - pr "\n"; - pr " memset (&rv, 0, sizeof rv);\n"; - pr "\n"; + ("vgremove", (RErr, [String "vgname"]), 78, [], + [InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["lvs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["vgs"]], [])], + "remove an LVM volume group", + "\ +Remove an LVM volume group C, (for example C). - (match snd style with - | [] -> - pr " serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n" - (String.uppercase shortname) - | args -> - List.iter ( - function - | String n -> - pr " args.%s = (char *) %s;\n" n n - | OptString n -> - pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n - | StringList n -> - pr " args.%s.%s_val = (char **) %s;\n" n n n; - pr " for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n; - | Bool n -> - pr " args.%s = %s;\n" n n - | Int n -> - pr " args.%s = %s;\n" n n - ) args; - pr " serial = dispatch (g, GUESTFS_PROC_%s,\n" - (String.uppercase shortname); - pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n" - name; - ); - pr " if (serial == -1)\n"; - pr " return %s;\n" error_code; - pr "\n"; +This also forcibly removes all logical volumes in the volume +group (if any)."); - pr " rv.cb_done = 0;\n"; - pr " g->reply_cb_internal = %s_cb;\n" shortname; - pr " g->reply_cb_internal_data = &rv;\n"; - pr " main_loop.main_loop_run (g);\n"; - pr " g->reply_cb_internal = NULL;\n"; - pr " g->reply_cb_internal_data = NULL;\n"; - pr " if (!rv.cb_done) {\n"; - pr " error (g, \"%s failed, see earlier error messages\");\n" name; - pr " return %s;\n" error_code; - pr " }\n"; - pr "\n"; + ("pvremove", (RErr, [String "device"]), 79, [], + [InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["pvremove"; "/dev/sda1"]; + ["lvs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["pvremove"; "/dev/sda1"]; + ["vgs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["pvremove"; "/dev/sda1"]; + ["pvs"]], [])], + "remove an LVM physical volume", + "\ +This wipes a physical volume C so that LVM will no longer +recognise it. + +The implementation uses the C command which refuses to +wipe physical volumes that contain any volume groups, so you have +to remove those first."); + + ("set_e2label", (RErr, [String "device"; String "label"]), 80, [], + [InitBasicFS, Always, TestOutput ( + [["set_e2label"; "/dev/sda1"; "testlabel"]; + ["get_e2label"; "/dev/sda1"]], "testlabel")], + "set the ext2/3/4 filesystem label", + "\ +This sets the ext2/3/4 filesystem label of the filesystem on +C to C