X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Fgenerator.ml;h=089ed29ec52964960248bee7e14feab24da56338;hb=90cf7fc904fca42665fe04cdd90a4c547d23b00c;hp=95a0985366b4d5120186bd922fc9bb5b8838da26;hpb=13339826ea01f8dbd581b5d2544e7692171cf386;p=libguestfs.git diff --git a/src/generator.ml b/src/generator.ml index 95a0985..bbdb330 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -15,53 +15,326 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + *) + +(* This script generates a large amount of code and documentation for + * all the daemon actions. * - * This script generates a large amount of code and documentation for - * all the daemon actions. To add a new action there are only two - * files you need to change, this one to describe the interface, and + * To add a new action there are only two files you need to change, + * this one to describe the interface (see the big table below), and * 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 the _source_ directory. + * + * IMPORTANT: This script should NOT print any warnings. If it prints + * warnings, you should treat them as errors. *) #load "unix.cma";; +#load "str.cma";; open Printf type style = ret * args and ret = - (* "Err" as a return value means an int used as a simple error + (* "RErr" as a return value means an int used as a simple error * indication, ie. 0 or -1. *) - | Err + | 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. - * Try to avoid using this. + * 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 - (* LVM PVs, VGs and LVs. *) - | RPVList of string - | RVGList of string - | RLVList of string -and args = - (* 0 arguments, 1 argument, etc. The guestfs_h param is implicit. *) - | P0 - | P1 of argt - | P2 of argt * argt + + (* "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 + * 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 + + (* "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 . 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. *) + + (* Note in future we should allow a "variable args" parameter as + * the final parameter, to allow commands like + * chmod mode file [file(s)...] + * This is not implemented yet, but many commands (such as chmod) + * are currently defined with the argument order keeping this future + * possibility in mind. + *) and argt = | String of string (* const char *name, cannot be NULL *) + | Device of string (* /dev device name, cannot be NULL *) + | Pathname of string (* file name, cannot be NULL *) + | Dev_or_Path of string (* /dev device name or Pathname, cannot be NULL *) | OptString of string (* const char *name, may be NULL *) + | StringList of string(* list of strings (each string cannot be NULL) *) + | DeviceList of string(* list of Device names (each cannot be NULL) *) | Bool of string (* boolean *) + | Int of string (* int (smallish ints, signed, <= 31 bits) *) + | Int64 of string (* any 64 bit int *) + (* 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 +(* Not implemented: + (* Opaque buffer which can contain arbitrary 8 bit data. + * In the C API, this is expressed as pair. + * Most other languages have a string type which can contain + * ASCII NUL. We use whatever type is appropriate for each + * language. + * Buffers are limited by the total message size. To transfer + * large blocks of data, use FileIn/FileOut parameters instead. + * To return an arbitrary buffer, use RBufferOut. + *) + | BufferIn of string +*) type flags = | ProtocolLimitWarning (* display warning about protocol size limits *) + | DangerWillRobinson (* flags particularly dangerous commands *) | FishAlias of string (* provide an alias for this cmd in guestfish *) | FishAction of string (* call this function in guestfish *) | NotInFish (* do not export via guestfish *) + | NotInDocs (* do not add this function to documentation *) + | DeprecatedBy of string (* function is deprecated, use .. instead *) + +(* You can supply zero or as many tests as you want per API call. + * + * Note that the test environment has 3 block devices, of size 500MB, + * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc), and + * a fourth ISO block device with some known files on it (/dev/sdd). + * + * Note for partitioning purposes, the 500MB device has 1015 cylinders. + * Number of cylinders was 63 for IDE emulated disks with precisely + * the same size. How exactly this is calculated is a mystery. + * + * The ISO block device (/dev/sdd) comes from images/test.iso. + * + * To be able to run the tests in a reasonable amount of time, + * the virtual machine and block devices are reused between tests. + * So don't try testing kill_subprocess :-x + * + * Between each test we blockdev-setrw, umount-all, lvm-remove-all. + * + * 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_prereq * test) list +and test = + (* Run the command sequence and just expect nothing to fail. *) + | TestRun of seq + + (* Run the command sequence and expect the output of the final + * command to be the string. + *) + | TestOutput of seq * string + + (* Run the command sequence and expect the output of the final + * command to be the list of strings. + *) + | TestOutputList of seq * string list + + (* Run the command sequence and expect the output of the final + * command to be the list of block devices (could be either + * "/dev/sd.." or "/dev/hd.." form - we don't check the 5th + * character of each string). + *) + | TestOutputListOfDevices of seq * string list + + (* Run the command sequence and expect the output of the final + * command to be the integer. + *) + | TestOutputInt of seq * int + + (* Run the command sequence and expect the output of the final + * command to be , eg. ">=", "1". + *) + | TestOutputIntOp of seq * string * int + + (* Run the command sequence and expect the output of the final + * command to be a true value (!= 0 or != NULL). + *) + | TestOutputTrue of seq + + (* Run the command sequence and expect the output of the final + * command to be a false value (== 0 or == NULL, but not an error). + *) + | TestOutputFalse of seq + + (* Run the command sequence and expect the output of the final + * command to be a list of the given length (but don't care about + * content). + *) + | 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 + + (* Run the command sequence and expect the final command (only) + * to fail. + *) + | TestLastFail of seq + +and test_field_compare = + | CompareWithInt of string * int + | CompareWithIntOp of string * string * int + | CompareWithString of string * string + | 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 + * LVM PVs, and some filesystems might be mounted. This is usually + * a bad idea. + *) + | InitNone + + (* Block devices are empty and no filesystems are mounted. *) + | InitEmpty + + (* /dev/sda contains a single partition /dev/sda1, with random + * content. /dev/sdb and /dev/sdc may have random content. + * No LVM. + *) + | InitPartition + + (* /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): + * formatted as ext2, empty [except for lost+found], mounted on / + * /dev/sdb and /dev/sdc may have random content. + *) + | InitBasicFSonLVM + + (* /dev/sdd (the ISO, see images/ directory in source) + * is mounted on / + *) + | InitISOFS + +(* Sequence of commands for testing. *) +and seq = cmd list +and cmd = string list (* Note about long descriptions: When referring to another * action, use the format C (ie. the full name of @@ -71,8 +344,94 @@ type flags = * Apart from that, long descriptions are just perldoc paragraphs. *) -let non_daemon_functions = [ - ("launch", (Err, P0), -1, [FishAlias "run"; FishAction "launch"], +(* Generate a random UUID (used in tests). *) +let uuidgen () = + let chan = Unix.open_process_in "uuidgen" in + let uuid = input_line chan in + (match Unix.close_process_in chan with + | Unix.WEXITED 0 -> () + | Unix.WEXITED _ -> + failwith "uuidgen: process exited with non-zero status" + | Unix.WSIGNALED _ | Unix.WSTOPPED _ -> + failwith "uuidgen: process signalled or stopped by signal" + ); + uuid + +(* These test functions are used in the language binding tests. *) + +let test_all_args = [ + String "str"; + OptString "optstr"; + StringList "strlist"; + Bool "b"; + Int "integer"; + Int64 "integer64"; + 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"; + "test0rconstoptstring", RConstOptString "valout"; + "test0rstring", RString "valout"; + "test0rstringlist", RStringList "valout"; + "test0rstruct", RStruct ("valout", "lvm_pv"); + "test0rstructlist", RStructList ("valout", "lvm_pv"); + "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", "\ Internally libguestfs is implemented by running a virtual machine @@ -81,21 +440,29 @@ using L. You should call this after configuring the handle (eg. adding drives) but before performing any actions."); - ("wait_ready", (Err, P0), -1, [NotInFish], - "wait until the qemu subprocess launches", + ("wait_ready", (RErr, []), -1, [NotInFish], + [], + "wait until the qemu subprocess launches (no op)", "\ -Internally libguestfs is implemented by running a virtual machine -using L. +This function is a no op. -You should call this after C to wait for the launch -to complete."); +In versions of the API E 1.0.71 you had to call this function +just after calling C to wait for the launch +to complete. However this is no longer necessary because +C now does the waiting. - ("kill_subprocess", (Err, P0), -1, [], +If you see any calls to this function in code then you can just +remove them, unless you want to retain compatibility with older +versions of the API."); + + ("kill_subprocess", (RErr, []), -1, [], + [], "kill the qemu subprocess", "\ This kills the qemu subprocess. You should never need to call this."); - ("add_drive", (Err, P1 (String "filename")), -1, [FishAlias "add"], + ("add_drive", (RErr, [String "filename"]), -1, [FishAlias "add"], + [], "add an image to examine or modify", "\ This function adds a virtual machine disk image C to the @@ -109,16 +476,52 @@ for whatever operations you want to perform (ie. read access if you just want to read the image or write access if you want to modify the image). -This is equivalent to the qemu parameter C<-drive file=filename>."); +This is equivalent to the qemu parameter +C<-drive file=filename,cache=off,if=...>. +C is omitted in cases where it is not supported by +the underlying filesystem. + +Note that this call checks for the existence of C. This +stops you from specifying other types of drive which are supported +by qemu such as C and C URLs. To specify those, use +the general C call instead."); - ("add_cdrom", (Err, P1 (String "filename")), -1, [FishAlias "cdrom"], + ("add_cdrom", (RErr, [String "filename"]), -1, [FishAlias "cdrom"], + [], "add a CD-ROM disk image to examine", "\ This function adds a virtual CD-ROM disk image to the guest. -This is equivalent to the qemu parameter C<-cdrom filename>."); +This is equivalent to the qemu parameter C<-cdrom filename>. - ("config", (Err, P2 (String "qemuparam", OptString "qemuvalue")), -1, [], +Note that this call checks for the existence of C. This +stops you from specifying other types of drive which are supported +by qemu such as C and C URLs. To specify those, use +the general C call instead."); + + ("add_drive_ro", (RErr, [String "filename"]), -1, [FishAlias "add-ro"], + [], + "add a drive in snapshot mode (read-only)", + "\ +This adds a drive in snapshot mode, making it effectively +read-only. + +Note that writes to the device are allowed, and will be seen for +the duration of the guestfs handle, but they are written +to a temporary file which is discarded as soon as the guestfs +handle is closed. We don't currently have any method to enable +changes to be committed, although qemu can support this. + +This is equivalent to the qemu parameter +C<-drive file=filename,snapshot=on,if=...>. + +Note that this call checks for the existence of C. This +stops you from specifying other types of drive which are supported +by qemu such as C and C URLs. To specify those, use +the general C call instead."); + + ("config", (RErr, [String "qemuparam"; OptString "qemuvalue"]), -1, [], + [], "add qemu parameters", "\ This can be used to add arbitrary qemu command line parameters @@ -130,7 +533,32 @@ The first character of C string must be a C<-> (dash). C can be NULL."); - ("set_path", (Err, P1 (String "path")), -1, [FishAlias "path"], + ("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, [], + [InitNone, Always, TestRun ( + [["get_qemu"]])], + "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 "searchpath"]), -1, [FishAlias "path"], + [], "set the search path", "\ Set the path that libguestfs searches for kernel and initrd.img. @@ -138,12 +566,11 @@ 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", P0), -1, [], + ("get_path", (RConstString "path", []), -1, [], + [InitNone, Always, TestRun ( + [["get_path"]])], "get the search path", "\ Return the current search path. @@ -151,19 +578,53 @@ Return the current search path. This is always non-NULL. If it wasn't set already, then this will return the default path."); - ("set_autosync", (Err, P1 (Bool "autosync")), -1, [FishAlias "autosync"], + ("set_append", (RErr, [OptString "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", (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. + *) + [], + "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). - ("get_autosync", (RBool "autosync", P0), -1, [], +This is disabled by default (except in guestfish where it is +enabled by default)."); + + ("get_autosync", (RBool "autosync", []), -1, [], + [InitNone, Always, TestRun ( + [["get_autosync"]])], "get autosync mode", "\ Get the autosync flag."); - ("set_verbose", (Err, P1 (Bool "verbose")), -1, [FishAlias "verbose"], + ("set_verbose", (RErr, [Bool "verbose"]), -1, [FishAlias "verbose"], + [], "set verbose mode", "\ If C is true, this turns on verbose messages (to C). @@ -171,14 +632,223 @@ If C is true, this turns on verbose messages (to C). Verbose messages are disabled unless the environment variable C is defined and set to C<1>."); - ("get_verbose", (RBool "verbose", P0), -1, [], + ("get_verbose", (RBool "verbose", []), -1, [], + [], "get verbose mode", "\ -This returns the verbose messages flag.") +This returns the verbose messages flag."); + + ("is_ready", (RBool "ready", []), -1, [], + [InitNone, Always, TestOutputTrue ( + [["is_ready"]])], + "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, [], + [InitNone, Always, TestOutputFalse ( + [["is_config"]])], + "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, [], + [InitNone, Always, TestOutputFalse ( + [["is_launching"]])], + "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, [], + [InitNone, Always, TestOutputFalse ( + [["is_busy"]])], + "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_memsize", (RErr, [Int "memsize"]), -1, [FishAlias "memsize"], + [InitNone, Always, TestOutputInt ( + [["set_memsize"; "500"]; + ["get_memsize"]], 500)], + "set memory allocated to the qemu subprocess", + "\ +This sets the memory size in megabytes allocated to the +qemu subprocess. This only has any effect if called before +C. + +You can also change this by setting the environment +variable C before the handle is +created. + +For more information on the architecture of libguestfs, +see L."); + + ("get_memsize", (RInt "memsize", []), -1, [], + [InitNone, Always, TestOutputIntOp ( + [["get_memsize"]], ">=", 256)], + "get memory allocated to the qemu subprocess", + "\ +This gets the memory size in megabytes allocated to the +qemu subprocess. + +If C was not called +on this handle, and if C was not set, +then this returns the compiled-in default value for memsize. + +For more information on the architecture of libguestfs, +see L."); + + ("get_pid", (RInt "pid", []), -1, [FishAlias "pid"], + [InitNone, Always, TestOutputIntOp ( + [["get_pid"]], ">=", 1)], + "get PID of qemu subprocess", + "\ +Return the process ID of the qemu subprocess. If there is no +qemu subprocess, then this will return an error. + +This is an internal call used for debugging and testing."); + + ("version", (RStruct ("version", "version"), []), -1, [], + [InitNone, Always, TestOutputStruct ( + [["version"]], [CompareWithInt ("major", 1)])], + "get the library version number", + "\ +Return the libguestfs version number that the program is linked +against. + +Note that because of dynamic linking this is not necessarily +the version of libguestfs that you compiled against. You can +compile the program, and then at runtime dynamically link +against a completely different C library. + +This call was added in version C<1.0.58>. In previous +versions of libguestfs there was no way to get the version +number. From C code you can use ELF weak linking tricks to find out if +this symbol exists (if it doesn't, then it's an earlier version). + +The call returns a structure with four elements. The first +three (C, C and C) are numbers and +correspond to the usual version triplet. The fourth element +(C) is a string and is normally empty, but may be +used for distro-specific information. + +To construct the original version string: +C<$major.$minor.$release$extra> + +I Don't use this call to test for availability +of features. Distro backports makes this unreliable."); + + ("set_selinux", (RErr, [Bool "selinux"]), -1, [FishAlias "selinux"], + [InitNone, Always, TestOutputTrue ( + [["set_selinux"; "true"]; + ["get_selinux"]])], + "set SELinux enabled or disabled at appliance boot", + "\ +This sets the selinux flag that is passed to the appliance +at boot time. The default is C (disabled). + +Note that if SELinux is enabled, it is always in +Permissive mode (C). + +For more information on the architecture of libguestfs, +see L."); + + ("get_selinux", (RBool "selinux", []), -1, [], + [], + "get SELinux enabled flag", + "\ +This returns the current setting of the selinux flag which +is passed to the appliance at boot time. See C. + +For more information on the architecture of libguestfs, +see L."); + + ("set_trace", (RErr, [Bool "trace"]), -1, [FishAlias "trace"], + [InitNone, Always, TestOutputFalse ( + [["set_trace"; "false"]; + ["get_trace"]])], + "enable or disable command traces", + "\ +If the command trace flag is set to 1, then commands are +printed on stdout before they are executed in a format +which is very similar to the one used by guestfish. In +other words, you can run a program with this enabled, and +you will get out a script which you can feed to guestfish +to perform the same set of actions. + +If you want to trace C API calls into libguestfs (and +other libraries) then possibly a better way is to use +the external ltrace(1) command. + +Command traces are disabled unless the environment variable +C is defined and set to C<1>."); + + ("get_trace", (RBool "trace", []), -1, [], + [], + "get command trace enabled flag", + "\ +Return the command trace flag."); + + ("set_direct", (RErr, [Bool "direct"]), -1, [FishAlias "direct"], + [InitNone, Always, TestOutputFalse ( + [["set_direct"; "false"]; + ["get_direct"]])], + "enable or disable direct appliance mode", + "\ +If the direct appliance mode flag is enabled, then stdin and +stdout are passed directly through to the appliance once it +is launched. + +One consequence of this is that log messages aren't caught +by the library and handled by C, +but go straight to stdout. + +You probably don't want to use this unless you know what you +are doing. + +The default is disabled."); + + ("get_direct", (RBool "direct", []), -1, [], + [], + "get direct appliance mode flag", + "\ +Return the direct appliance mode flag."); + ] +(* daemon_functions are any functions which cause some action + * to take place in the daemon. + *) + let daemon_functions = [ - ("mount", (Err, P2 (String "device", String "mountpoint")), 1, [], + ("mount", (RErr, [Device "device"; String "mountpoint"]), 1, [], + [InitEmpty, Always, TestOutput ( + [["sfdiskM"; "/dev/sda"; ","]; + ["mkfs"; "ext2"; "/dev/sda1"]; + ["mount"; "/dev/sda1"; "/"]; + ["write_file"; "/new"; "new file contents"; "0"]; + ["cat"; "/new"]], "new file contents")], "mount a guest disk at a position in the filesystem", "\ Mount a guest disk at a position in the filesystem. Block devices @@ -198,7 +868,8 @@ on the underlying device. The filesystem options C and C are set with this call, in order to improve reliability."); - ("sync", (Err, P0), 2, [], + ("sync", (RErr, []), 2, [], + [ 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 @@ -207,14 +878,19 @@ underlying disk image. You should always call this if you have modified a disk image, before closing the handle."); - ("touch", (Err, P1 (String "path")), 3, [], + ("touch", (RErr, [Pathname "path"]), 3, [], + [InitBasicFS, Always, TestOutputTrue ( + [["touch"; "/new"]; + ["exists"; "/new"]])], "update file timestamps or create a new file", "\ Touch acts like the L command. It can be used to update the timestamps on a file, or, if the file does not exist, to create a new zero-length file."); - ("cat", (RString "content", P1 (String "path")), 4, [ProtocolLimitWarning], + ("cat", (RString "content", [Pathname "path"]), 4, [ProtocolLimitWarning], + [InitISOFS, Always, TestOutput ( + [["cat"; "/known-2"]], "abcdef\n")], "list the contents of a file", "\ Return the contents of the file named C. @@ -222,9 +898,12 @@ 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."); +or C functions which have a more complex interface."); - ("ll", (RString "listing", P1 (String "directory")), 5, [], + ("ll", (RString "listing", [Pathname "directory"]), 5, [], + [], (* XXX Tricky to test because it depends on the exact format + * of the 'ls -l' command, which changes between F10 and F11. + *) "list the files in a directory (long format)", "\ List the files in C (relative to the root directory, @@ -233,7 +912,12 @@ there is no cwd) in the format of 'ls -la'. This command is mostly useful for interactive sessions. It is I intended that you try to parse the output string."); - ("ls", (RStringList "listing", P1 (String "directory")), 6, [], + ("ls", (RStringList "listing", [Pathname "directory"]), 6, [], + [InitBasicFS, Always, TestOutputList ( + [["touch"; "/new"]; + ["touch"; "/newer"]; + ["touch"; "/newest"]; + ["ls"; "/"]], ["lost+found"; "new"; "newer"; "newest"])], "list the files in a directory", "\ List the files in C (relative to the root directory, @@ -243,14 +927,21 @@ hidden files are shown. This command is mostly useful for interactive sessions. Programs should probably use C instead."); - ("list_devices", (RStringList "devices", P0), 7, [], + ("list_devices", (RStringList "devices", []), 7, [], + [InitEmpty, Always, TestOutputListOfDevices ( + [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"; "/dev/sdd"])], "list the block devices", "\ List all the block devices. The full block device names are returned, eg. C"); - ("list_partitions", (RStringList "partitions", P0), 8, [], + ("list_partitions", (RStringList "partitions", []), 8, [], + [InitBasicFS, Always, TestOutputListOfDevices ( + [["list_partitions"]], ["/dev/sda1"]); + InitEmpty, Always, TestOutputListOfDevices ( + [["sfdiskM"; "/dev/sda"; ",100 ,200 ,"]; + ["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])], "list the partitions", "\ List all the partitions detected on all block devices. @@ -260,7 +951,15 @@ The full partition device names are returned, eg. C This does not return logical volumes. For that you will need to call C."); - ("pvs", (RStringList "physvols", P0), 9, [], + ("pvs", (RStringList "physvols", []), 9, [], + [InitBasicFSonLVM, Always, TestOutputListOfDevices ( + [["pvs"]], ["/dev/sda1"]); + InitEmpty, Always, TestOutputListOfDevices ( + [["sfdiskM"; "/dev/sda"; ",100 ,200 ,"]; + ["pvcreate"; "/dev/sda1"]; + ["pvcreate"; "/dev/sda2"]; + ["pvcreate"; "/dev/sda3"]; + ["pvs"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])], "list the LVM physical volumes (PVs)", "\ List all the physical volumes detected. This is the equivalent @@ -271,7 +970,17 @@ PVs (eg. C). See also C."); - ("vgs", (RStringList "volgroups", P0), 10, [], + ("vgs", (RStringList "volgroups", []), 10, [], + [InitBasicFSonLVM, Always, TestOutputList ( + [["vgs"]], ["VG"]); + InitEmpty, Always, TestOutputList ( + [["sfdiskM"; "/dev/sda"; ",100 ,200 ,"]; + ["pvcreate"; "/dev/sda1"]; + ["pvcreate"; "/dev/sda2"]; + ["pvcreate"; "/dev/sda3"]; + ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"]; + ["vgcreate"; "VG2"; "/dev/sda3"]; + ["vgs"]], ["VG1"; "VG2"])], "list the LVM volume groups (VGs)", "\ List all the volumes groups detected. This is the equivalent @@ -282,7 +991,20 @@ detected (eg. C). See also C."); - ("lvs", (RStringList "logvols", P0), 11, [], + ("lvs", (RStringList "logvols", []), 11, [], + [InitBasicFSonLVM, Always, TestOutputList ( + [["lvs"]], ["/dev/VG/LV"]); + InitEmpty, Always, TestOutputList ( + [["sfdiskM"; "/dev/sda"; ",100 ,200 ,"]; + ["pvcreate"; "/dev/sda1"]; + ["pvcreate"; "/dev/sda2"]; + ["pvcreate"; "/dev/sda3"]; + ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"]; + ["vgcreate"; "VG2"; "/dev/sda3"]; + ["lvcreate"; "LV1"; "VG1"; "50"]; + ["lvcreate"; "LV2"; "VG1"; "50"]; + ["lvcreate"; "LV3"; "VG2"; "50"]; + ["lvs"]], ["/dev/VG1/LV1"; "/dev/VG1/LV2"; "/dev/VG2/LV3"])], "list the LVM logical volumes (LVs)", "\ List all the logical volumes detected. This is the equivalent @@ -293,227 +1015,3458 @@ This returns a list of the logical volume device names See also C."); - ("pvs_full", (RPVList "physvols", P0), 12, [], + ("pvs_full", (RStructList ("physvols", "lvm_pv"), []), 12, [], + [], (* XXX how to test? *) "list the LVM physical volumes (PVs)", "\ List all the physical volumes detected. This is the equivalent of the L command. The \"full\" version includes all fields."); - ("vgs_full", (RVGList "volgroups", P0), 13, [], + ("vgs_full", (RStructList ("volgroups", "lvm_vg"), []), 13, [], + [], (* XXX how to test? *) "list the LVM volume groups (VGs)", "\ List all the volumes groups detected. This is the equivalent of the L command. The \"full\" version includes all fields."); - ("lvs_full", (RLVList "logvols", P0), 14, [], + ("lvs_full", (RStructList ("logvols", "lvm_lv"), []), 14, [], + [], (* XXX how to test? *) "list the LVM logical volumes (LVs)", "\ List all the logical volumes detected. This is the equivalent of the L command. The \"full\" version includes all fields."); -] -let all_functions = non_daemon_functions @ daemon_functions + ("read_lines", (RStringList "lines", [Pathname "path"]), 15, [], + [InitISOFS, Always, TestOutputList ( + [["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]); + InitISOFS, Always, TestOutputList ( + [["read_lines"; "/empty"]], [])], + "read file as lines", + "\ +Return the contents of the file named C. -(* 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 - -(* 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; -] +The file contents are returned as a list of lines. Trailing +C and C character sequences are I returned. -(* 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 +Note that this function cannot correctly handle binary files +(specifically, files containing C<\\0> character which is treated +as end of line). For those you need to use the C +function which has a more complex interface."); -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 + ("aug_init", (RErr, [Pathname "root"; Int "flags"]), 16, [], + [], (* XXX Augeas code needs tests. *) + "create a new Augeas handle", + "\ +Create a new Augeas handle for editing configuration files. +If there was any previous Augeas handle associated with this +guestfs session, then it is closed. -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 +You must call this before using any other C +commands. -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 - ) +C is the filesystem root. C must not be NULL, +use C instead. -let rec find_map f = function - | [] -> raise Not_found - | x :: xs -> - match f x with - | Some y -> y - | None -> find_map f xs +The flags are the same as the flags defined in +Eaugeas.hE, the logical I of the following +integers: -let iteri f xs = - let rec loop i = function - | [] -> () - | x :: xs -> f i x; loop (i+1) xs - in - loop 0 xs +=over 4 -(* 'pr' prints to the current output file. *) -let chan = ref stdout -let pr fs = ksprintf (output_string !chan) fs +=item C = 1 -let iter_args f = function - | P0 -> () - | P1 arg1 -> f arg1 - | P2 (arg1, arg2) -> f arg1; f arg2 +Keep the original file with a C<.augsave> extension. -let iteri_args f = function - | P0 -> () - | P1 arg1 -> f 0 arg1 - | P2 (arg1, arg2) -> f 0 arg1; f 1 arg2 +=item C = 2 -let map_args f = function - | P0 -> [] - | P1 arg1 -> [f arg1] - | P2 (arg1, arg2) -> [f arg1; f arg2] +Save changes into a file with extension C<.augnew>, and +do not overwrite original. Overrides C. -let nr_args = function | P0 -> 0 | P1 _ -> 1 | P2 _ -> 2 +=item C = 4 -(* Check function names etc. for consistency. *) -let check_functions () = - List.iter ( - fun (name, _, _, _, _, longdesc) -> - if String.contains name '-' then - failwithf "function name '%s' should not contain '-', use '_' instead." - name; - if longdesc.[String.length longdesc-1] = '\n' then - failwithf "long description of %s should not end with \\n." name - ) all_functions; +Typecheck lenses (can be expensive). - List.iter ( - fun (name, _, proc_nr, _, _, _) -> - if proc_nr <= 0 then - failwithf "daemon function %s should have proc_nr > 0" name - ) daemon_functions; +=item C = 8 - 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; +Do not use standard load path for modules. - 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 +=item C = 16 -type comment_style = CStyle | HashStyle | OCamlStyle -type license = GPLv2 | LGPLv2 +Make save a no-op, just record what would have been changed. -(* Generate a header block in a number of standard styles. *) -let rec generate_header comment license = - let c = match comment with - | CStyle -> pr "/* "; " *" - | HashStyle -> pr "# "; "#" - | OCamlStyle -> pr "(* "; " *" in - pr "libguestfs generated file\n"; +=item C = 32 + +Do not load the tree in C. + +=back + +To close the handle, you can call C. + +To find out more about Augeas, see L."); + + ("aug_close", (RErr, []), 26, [], + [], (* XXX Augeas code needs tests. *) + "close the current Augeas handle", + "\ +Close the current Augeas handle and free up any resources +used by it. After calling this, you have to call +C again before you can use any other +Augeas functions."); + + ("aug_defvar", (RInt "nrnodes", [String "name"; OptString "expr"]), 17, [], + [], (* XXX Augeas code needs tests. *) + "define an Augeas variable", + "\ +Defines an Augeas variable C whose value is the result +of evaluating C. If C is NULL, then C is +undefined. + +On success this returns the number of nodes in C, or +C<0> if C evaluates to something which is not a nodeset."); + + ("aug_defnode", (RStruct ("nrnodescreated", "int_bool"), [String "name"; String "expr"; String "val"]), 18, [], + [], (* XXX Augeas code needs tests. *) + "define an Augeas node", + "\ +Defines a variable C whose value is the result of +evaluating C. + +If C evaluates to an empty nodeset, a node is created, +equivalent to calling C C, C. +C will be the nodeset containing that single node. + +On success this returns a pair containing the +number of nodes in the nodeset, and a boolean flag +if a node was created."); + + ("aug_get", (RString "val", [String "augpath"]), 19, [], + [], (* XXX Augeas code needs tests. *) + "look up the value of an Augeas path", + "\ +Look up the value associated with C. If C +matches exactly one node, the C is returned."); + + ("aug_set", (RErr, [String "augpath"; String "val"]), 20, [], + [], (* XXX Augeas code needs tests. *) + "set Augeas path to value", + "\ +Set the value associated with C to C."); + + ("aug_insert", (RErr, [String "augpath"; String "label"; Bool "before"]), 21, [], + [], (* XXX Augeas code needs tests. *) + "insert a sibling Augeas node", + "\ +Create a new sibling C