From b4d2a01828e5de85e5eee3631f7fe3925a0312ca Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Fri, 10 Apr 2009 18:25:07 +0100 Subject: Added test suite. --- src/generator.ml | 1046 +++++++++++++++++++++++++++++++++++++++++------- src/guestfs-actions.c | 931 ++++++++++++++++++++++++++++++++++++++++++ src/guestfs-actions.h | 13 + src/guestfs_protocol.c | 208 ++++++++++ src/guestfs_protocol.h | 132 +++++- src/guestfs_protocol.x | 79 ++++ 6 files changed, 2271 insertions(+), 138 deletions(-) (limited to 'src') diff --git a/src/generator.ml b/src/generator.ml index 06a638e6..6792e07a 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -77,59 +77,74 @@ and args = argt list (* Function parameters, guestfs handle is implicit. *) and argt = | String of string (* const char *name, cannot be NULL *) | OptString of string (* const char *name, may be NULL *) + | StringList of string(* list of strings (each string cannot be NULL) *) | Bool of string (* boolean *) | Int of string (* int (smallish ints, signed, <= 31 bits) *) 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 *) +let protocol_limit_warning = + "Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP." + +let danger_will_robinson = + "B." + (* 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 10M, 20M - * and 30M (respectively /dev/sda, /dev/sdb, /dev/sdc). 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 + * Note that the test environment has 3 block devices, of size 500MB, + * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc). + * Note for partitioning purposes, the 500MB device has 63 cylinders. + * + * 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 umount-all and lvm-remove-all. * * Don't assume anything about the previous contents of the block * devices. Use 'Init*' to create some initial scenarios. *) -type tests = test list +type tests = (test_init * test) list and test = (* Run the command sequence and just expect nothing to fail. *) - | TestRun of test_init * seq + | TestRun of seq (* Run the command sequence and expect the output of the final * command to be the string. *) - | TestOutput of test_init * seq * 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 test_init * seq * string list + | TestOutputList of seq * string list (* Run the command sequence and expect the output of the final * command to be the integer. *) - | TestOutputInt of test_init * seq * int + | TestOutputInt of seq * int (* Run the command sequence and expect the output of the final * command to be a true value (!= 0 or != NULL). *) - | TestOutputTrue of test_init * seq + | 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 test_init * seq + | 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 test_init * seq * int + | TestOutputLength of seq * int (* Run the command sequence and expect the final command (only) * to fail. *) - | TestLastFail of test_init * seq + | TestLastFail of seq (* Some initial scenarios for testing. *) and test_init = @@ -143,7 +158,7 @@ and test_init = | InitEmpty (* /dev/sda: * /dev/sda1 (is a PV): - * /dev/VG/LV: + * /dev/VG/LV (size 8MB): * formatted as ext2, empty [except for lost+found], mounted on / * /dev/sdb and /dev/sdc may have random content. *) @@ -281,9 +296,8 @@ This returns the verbose messages flag.") let daemon_functions = [ ("mount", (RErr, [String "device"; String "mountpoint"]), 1, [], - [TestOutput ( - InitNone, - [["sfdisk"]; + [InitNone, TestOutput ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["write_file"; "/new"; "new file contents"; "0"]; @@ -308,7 +322,7 @@ The filesystem options C and C are set with this call, in order to improve reliability."); ("sync", (RErr, []), 2, [], - [ TestRun (InitNone, [["sync"]])], + [ InitNone, 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 @@ -318,8 +332,7 @@ You should always call this if you have modified a disk image, before closing the handle."); ("touch", (RErr, [String "path"]), 3, [], - [TestOutputTrue ( - InitEmpty, + [InitEmpty, TestOutputTrue ( [["touch"; "/new"]; ["exists"; "/new"]])], "update file timestamps or create a new file", @@ -329,8 +342,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], - [TestOutput ( - InitEmpty, + [InitEmpty, TestOutput ( [["write_file"; "/new"; "new file contents"; "0"]; ["cat"; "/new"]], "new file contents")], "list the contents of a file", @@ -355,8 +367,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, [], - [TestOutputList ( - InitEmpty, + [InitEmpty, TestOutputList ( [["touch"; "/new"]; ["touch"; "/newer"]; ["touch"; "/newest"]; @@ -371,8 +382,7 @@ This command is mostly useful for interactive sessions. Programs should probably use C instead."); ("list_devices", (RStringList "devices", []), 7, [], - [TestOutputList ( - InitNone, + [InitNone, TestOutputList ( [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"])], "list the block devices", "\ @@ -381,12 +391,10 @@ List all the block devices. The full block device names are returned, eg. C"); ("list_partitions", (RStringList "partitions", []), 8, [], - [TestOutputList ( - InitEmpty, + [InitEmpty, TestOutputList ( [["list_partitions"]], ["/dev/sda1"]); - TestOutputList ( - InitEmpty, - [["sfdisk"]; + InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])], "list the partitions", "\ @@ -398,12 +406,10 @@ This does not return logical volumes. For that you will need to call C."); ("pvs", (RStringList "physvols", []), 9, [], - [TestOutputList ( - InitEmptyLVM, + [InitEmptyLVM, TestOutputList ( [["pvs"]], ["/dev/sda1"]); - TestOutputList ( - InitNone, - [["sfdisk"]; + InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; ["pvcreate"; "/dev/sda3"]; @@ -419,12 +425,10 @@ PVs (eg. C). See also C."); ("vgs", (RStringList "volgroups", []), 10, [], - [TestOutputList ( - InitEmptyLVM, + [InitEmptyLVM, TestOutputList ( [["vgs"]], ["VG"]); - TestOutputList ( - InitNone, - [["sfdisk"]; + InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; ["pvcreate"; "/dev/sda3"]; @@ -442,21 +446,19 @@ detected (eg. C). See also C."); ("lvs", (RStringList "logvols", []), 11, [], - [TestOutputList ( - InitEmptyLVM, + [InitEmptyLVM, TestOutputList ( [["lvs"]], ["/dev/VG/LV"]); - TestOutputList ( - InitNone, - [["sfdisk"]; + InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; ["pvcreate"; "/dev/sda3"]; ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"]; ["vgcreate"; "VG2"; "/dev/sda3"]; - ["lvcreate"; "LV1"; "VG1"; "5000"]; - ["lvcreate"; "LV2"; "VG1"; "5000"]; - ["lvcreate"; "LV3"; "VG2"; "5000"]; - ["lvs"]], ["LV1"; "LV2"; "LV3"])], + ["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 @@ -468,8 +470,7 @@ This returns a list of the logical volume device names See also C."); ("pvs_full", (RPVList "physvols", []), 12, [], - [TestOutputLength ( - InitEmptyLVM, + [InitEmptyLVM, TestOutputLength ( [["pvs"]], 1)], "list the LVM physical volumes (PVs)", "\ @@ -477,8 +478,7 @@ List all the physical volumes detected. This is the equivalent of the L command. The \"full\" version includes all fields."); ("vgs_full", (RVGList "volgroups", []), 13, [], - [TestOutputLength ( - InitEmptyLVM, + [InitEmptyLVM, TestOutputLength ( [["pvs"]], 1)], "list the LVM volume groups (VGs)", "\ @@ -486,8 +486,7 @@ List all the volumes groups detected. This is the equivalent of the L command. The \"full\" version includes all fields."); ("lvs_full", (RLVList "logvols", []), 14, [], - [TestOutputLength ( - InitEmptyLVM, + [InitEmptyLVM, TestOutputLength ( [["pvs"]], 1)], "list the LVM logical volumes (LVs)", "\ @@ -495,12 +494,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, [], - [TestOutputList ( - InitEmpty, + [InitEmpty, TestOutputList ( [["write_file"; "/new"; "line1\r\nline2\nline3"; "0"]; ["read_lines"; "/new"]], ["line1"; "line2"; "line3"]); - TestOutputList ( - InitEmpty, + InitEmpty, TestOutputList ( [["write_file"; "/new"; ""; "0"]; ["read_lines"; "/new"]], [])], "read file as lines", @@ -675,45 +672,38 @@ This is just a shortcut for listing C C and sorting the resulting nodes into alphabetical order."); ("rm", (RErr, [String "path"]), 29, [], - [TestRun ( - InitEmpty, + [InitEmpty, TestRun [["touch"; "/new"]; - ["rm"; "/new"]]); - TestLastFail ( - InitEmpty, - [["rm"; "/new"]]); - TestLastFail ( - InitEmpty, + ["rm"; "/new"]]; + InitEmpty, TestLastFail + [["rm"; "/new"]]; + InitEmpty, TestLastFail [["mkdir"; "/new"]; - ["rm"; "/new"]])], + ["rm"; "/new"]]], "remove a file", "\ Remove the single file C."); ("rmdir", (RErr, [String "path"]), 30, [], - [TestRun ( - InitEmpty, + [InitEmpty, TestRun [["mkdir"; "/new"]; - ["rmdir"; "/new"]]); - TestLastFail ( - InitEmpty, - [["rmdir"; "/new"]]); - TestLastFail ( - InitEmpty, + ["rmdir"; "/new"]]; + InitEmpty, TestLastFail + [["rmdir"; "/new"]]; + InitEmpty, TestLastFail [["touch"; "/new"]; - ["rmdir"; "/new"]])], + ["rmdir"; "/new"]]], "remove a directory", "\ Remove the single directory C."); ("rm_rf", (RErr, [String "path"]), 31, [], - [TestOutputFalse ( - InitEmpty, + [InitEmpty, TestOutputFalse [["mkdir"; "/new"]; ["mkdir"; "/new/foo"]; ["touch"; "/new/foo/bar"]; ["rm_rf"; "/new"]; - ["exists"; "/new"]])], + ["exists"; "/new"]]], "remove a file or directory recursively", "\ Remove the file or directory C, recursively removing the @@ -721,27 +711,25 @@ contents if its a directory. This is like the C shell command."); ("mkdir", (RErr, [String "path"]), 32, [], - [TestOutputTrue ( - InitEmpty, + [InitEmpty, TestOutputTrue [["mkdir"; "/new"]; - ["is_dir"; "/new"]])], + ["is_dir"; "/new"]]; + InitEmpty, TestLastFail + [["mkdir"; "/new/foo/bar"]]], "create a directory", "\ Create a directory named C."); ("mkdir_p", (RErr, [String "path"]), 33, [], - [TestOutputTrue ( - InitEmpty, + [InitEmpty, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; - ["is_dir"; "/new/foo/bar"]]); - TestOutputTrue ( - InitEmpty, + ["is_dir"; "/new/foo/bar"]]; + InitEmpty, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; - ["is_dir"; "/new/foo"]]); - TestOutputTrue ( - InitEmpty, + ["is_dir"; "/new/foo"]]; + InitEmpty, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; - ["is_dir"; "/new"]])], + ["is_dir"; "/new"]]], "create a directory and parents", "\ Create a directory named C, creating any parent directories @@ -763,6 +751,200 @@ Change the file owner to C and group to C. Only numeric uid and gid are supported. If you want to use names, you will need to locate and parse the password file yourself (Augeas support makes this relatively easy)."); + + ("exists", (RBool "existsflag", [String "path"]), 36, [], + [InitEmpty, TestOutputTrue ( + [["touch"; "/new"]; + ["exists"; "/new"]]); + InitEmpty, TestOutputTrue ( + [["mkdir"; "/new"]; + ["exists"; "/new"]])], + "test if file or directory exists", + "\ +This returns C if and only if there is a file, directory +(or anything) with the given C name. + +See also C, C, C."); + + ("is_file", (RBool "fileflag", [String "path"]), 37, [], + [InitEmpty, TestOutputTrue ( + [["touch"; "/new"]; + ["is_file"; "/new"]]); + InitEmpty, TestOutputFalse ( + [["mkdir"; "/new"]; + ["is_file"; "/new"]])], + "test if file exists", + "\ +This returns C if and only if there is a file +with the given C name. Note that it returns false for +other objects like directories. + +See also C."); + + ("is_dir", (RBool "dirflag", [String "path"]), 38, [], + [InitEmpty, TestOutputFalse ( + [["touch"; "/new"]; + ["is_dir"; "/new"]]); + InitEmpty, TestOutputTrue ( + [["mkdir"; "/new"]; + ["is_dir"; "/new"]])], + "test if file exists", + "\ +This returns C if and only if there is a directory +with the given C name. Note that it returns false for +other objects like files. + +See also C."); + + ("pvcreate", (RErr, [String "device"]), 39, [], + [InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; + ["pvcreate"; "/dev/sda1"]; + ["pvcreate"; "/dev/sda2"]; + ["pvcreate"; "/dev/sda3"]; + ["pvs"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])], + "create an LVM physical volume", + "\ +This creates an LVM physical volume on the named C, +where C should usually be a partition name such +as C."); + + ("vgcreate", (RErr, [String "volgroup"; StringList "physvols"]), 40, [], + [InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; + ["pvcreate"; "/dev/sda1"]; + ["pvcreate"; "/dev/sda2"]; + ["pvcreate"; "/dev/sda3"]; + ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"]; + ["vgcreate"; "VG2"; "/dev/sda3"]; + ["vgs"]], ["VG1"; "VG2"])], + "create an LVM volume group", + "\ +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, [], + [InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; + ["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"]; + ["lvcreate"; "LV4"; "VG2"; "50"]; + ["lvcreate"; "LV5"; "VG2"; "50"]; + ["lvs"]], + ["/dev/VG1/LV1"; "/dev/VG1/LV2"; + "/dev/VG2/LV3"; "/dev/VG2/LV4"; "/dev/VG2/LV5"])], + "create an LVM volume group", + "\ +This creates an LVM volume group called C +on the volume group C, with C megabytes."); + + ("mkfs", (RErr, [String "fstype"; String "device"]), 42, [], + [InitNone, 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")], + "make a filesystem", + "\ +This creates a filesystem on C (usually a partition +of LVM logical volume). The filesystem type is C, for +example C."); + + ("sfdisk", (RErr, [String "device"; + Int "cyls"; Int "heads"; Int "sectors"; + StringList "lines"]), 43, [DangerWillRobinson], + [], + "create partitions on a block device", + "\ +This is a direct interface to the L program for creating +partitions on block devices. + +C should be a block device, for example C. + +C, C and C are the number of cylinders, heads +and sectors on the device, which are passed directly to sfdisk as +the I<-C>, I<-H> and I<-S> parameters. If you pass C<0> for any +of these, then the corresponding parameter is omitted. Usually for +'large' disks, you can just pass C<0> for these, but for small +(floppy-sized) disks, sfdisk (or rather, the kernel) cannot work +out the right geometry and you will need to tell it. + +C is a list of lines that we feed to C. For more +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)."); + + ("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning], + [InitNone, 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")], + "Create a file", + "\ +This call creates a file called C. The contents of the +file is the string C (which can contain any 8 bit data), +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)."); + + ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"], + [InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["mkfs"; "ext2"; "/dev/sda1"]; + ["mount"; "/dev/sda1"; "/"]; + ["mounts"]], ["/dev/sda1"]); + InitNone, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["mkfs"; "ext2"; "/dev/sda1"]; + ["mount"; "/dev/sda1"; "/"]; + ["umount"; "/"]; + ["mounts"]], [])], + "unmount a filesystem", + "\ +This unmounts the given filesystem. The filesystem may be +specified either by its mountpoint (path) or the device which +contains the filesystem."); + + ("mounts", (RStringList "devices", []), 46, [], + [InitEmpty, TestOutputList ( + [["mounts"]], ["/dev/sda1"])], + "show mounted filesystems", + "\ +This returns the list of currently mounted filesystems. It returns +the list of devices (eg. C, C). + +Some internal mounts are not shown."); + + ("umount_all", (RErr, []), 47, [FishAlias "unmount-all"], + [InitEmpty, TestOutputList ( + [["umount_all"]; + ["mounts"]], [])], + "unmount all filesystems", + "\ +This unmounts all mounted filesystems. + +Some internal mounts are not unmounted by this call."); + + ("lvm_remove_all", (RErr, []), 48, [DangerWillRobinson], + [], + "remove all LVM LVs, VGs and PVs", + "\ +This command removes all LVM logical volumes, volume groups +and physical volumes."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -884,6 +1066,17 @@ let rec replace_str s s1 s2 = s' ^ s2 ^ replace_str s'' s1 s2 ) +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'' + ) + let rec find_map f = function | [] -> raise Not_found | x :: xs -> @@ -898,7 +1091,15 @@ let iteri f xs = in loop 0 xs -let name_of_argt = function String n | OptString n | Bool n | Int n -> n +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 + +let name_of_argt = function + | String n | OptString n | StringList n | Bool n | Int n -> n (* Check function names etc. for consistency. *) let check_functions () = @@ -1089,9 +1290,9 @@ I after use>.\n\n" I after use>.\n\n" ); if List.mem ProtocolLimitWarning flags then - pr "Because of the message protocol, there is a transfer limit -of somewhere between 2MB and 4MB. To transfer large files you should use -FTP.\n\n"; + pr "%s\n\n" protocol_limit_warning; + if List.mem DangerWillRobinson flags then + pr "%s\n\n" danger_will_robinson; ) all_functions_sorted and generate_structs_pod () = @@ -1169,6 +1370,7 @@ and generate_xdr () = 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; @@ -1427,6 +1629,9 @@ and generate_client_actions () = 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 -> @@ -1556,6 +1761,7 @@ and generate_daemon_actions () = function | String n | OptString n -> pr " const char *%s;\n" n + | StringList n -> pr " char **%s;\n" n | Bool n -> pr " int %s;\n" n | Int n -> pr " int %s;\n" n ) args @@ -1575,6 +1781,10 @@ and generate_daemon_actions () = function | String n -> pr " %s = args.%s;\n" n n | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n + | StringList n -> + pr " args.%s.%s_val = realloc (args.%s.%s_val, sizeof (char *) * (args.%s.%s_len+1));\n" n n n n n n; + pr " args.%s.%s_val[args.%s.%s_len] = NULL;\n" n n n n; + pr " %s = args.%s.%s_val;\n" n n n | Bool n -> pr " %s = args.%s;\n" n n | Int n -> pr " %s = args.%s;\n" n n ) args; @@ -1586,8 +1796,8 @@ and generate_daemon_actions () = pr ";\n"; pr " if (r == %s)\n" error_code; - pr " /* do_%s has already called reply_with_error, so just return */\n" name; - pr " return;\n"; + pr " /* do_%s has already called reply_with_error */\n" name; + pr " goto done;\n"; pr "\n"; (match fst style with @@ -1633,6 +1843,16 @@ and generate_daemon_actions () = pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name ); + (* Free the args. *) + (match snd style with + | [] -> + pr "done: ;\n"; + | _ -> + pr "done:\n"; + pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n" + name + ); + pr "}\n\n"; ) daemon_functions; @@ -1823,20 +2043,459 @@ and generate_daemon_actions () = and generate_tests () = generate_header CStyle GPLv2; - pr "#include \n"; - pr "#include \n"; - pr "#include \n"; - pr "\n"; - pr "#include \"guestfs.h\"\n"; + pr "\ +#include +#include +#include +#include +#include +#include + +#include \"guestfs.h\" + +static guestfs_h *g; +static int suppress_error = 0; + +static void print_error (guestfs_h *g, void *data, const char *msg) +{ + if (!suppress_error) + fprintf (stderr, \"%%s\\n\", msg); +} + +static void print_strings (char * const * const argv) +{ + int argc; + + for (argc = 0; argv[argc] != NULL; ++argc) + printf (\"\\t%%s\\n\", argv[argc]); +} + +"; + + let test_names = + List.map ( + fun (name, _, _, _, tests, _, _) -> + mapi (generate_one_test name) tests + ) all_functions in + let test_names = List.concat test_names in + let nr_tests = List.length test_names in + + pr "\ +int main (int argc, char *argv[]) +{ + char c = 0; + int failed = 0; + const char *srcdir; + int fd; + char buf[256]; + + g = guestfs_create (); + if (g == NULL) { + printf (\"guestfs_create FAILED\\n\"); + exit (1); + } + + guestfs_set_error_handler (g, print_error, NULL); + + srcdir = getenv (\"srcdir\"); + if (!srcdir) srcdir = \".\"; + guestfs_set_path (g, srcdir); + + snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir); + fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + if (fd == -1) { + perror (buf); + exit (1); + } + if (lseek (fd, %d, SEEK_SET) == -1) { + perror (\"lseek\"); + close (fd); + unlink (buf); + exit (1); + } + if (write (fd, &c, 1) == -1) { + perror (\"write\"); + close (fd); + unlink (buf); + exit (1); + } + if (close (fd) == -1) { + perror (buf); + unlink (buf); + exit (1); + } + if (guestfs_add_drive (g, buf) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", buf); + exit (1); + } + + snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir); + fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + if (fd == -1) { + perror (buf); + exit (1); + } + if (lseek (fd, %d, SEEK_SET) == -1) { + perror (\"lseek\"); + close (fd); + unlink (buf); + exit (1); + } + if (write (fd, &c, 1) == -1) { + perror (\"write\"); + close (fd); + unlink (buf); + exit (1); + } + if (close (fd) == -1) { + perror (buf); + unlink (buf); + exit (1); + } + if (guestfs_add_drive (g, buf) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", buf); + exit (1); + } + + snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir); + fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + if (fd == -1) { + perror (buf); + exit (1); + } + if (lseek (fd, %d, SEEK_SET) == -1) { + perror (\"lseek\"); + close (fd); + unlink (buf); + exit (1); + } + if (write (fd, &c, 1) == -1) { + perror (\"write\"); + close (fd); + unlink (buf); + exit (1); + } + if (close (fd) == -1) { + perror (buf); + unlink (buf); + exit (1); + } + if (guestfs_add_drive (g, buf) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", buf); + exit (1); + } + + if (guestfs_launch (g) == -1) { + printf (\"guestfs_launch FAILED\\n\"); + exit (1); + } + if (guestfs_wait_ready (g) == -1) { + printf (\"guestfs_wait_ready FAILED\\n\"); + exit (1); + } + +" (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024); + + iteri ( + fun i test_name -> + pr " printf (\"%3d/%3d %s\\n\");\n" (i+1) nr_tests test_name; + pr " if (%s () == -1) {\n" test_name; + pr " printf (\"%s FAILED\\n\");\n" test_name; + pr " failed++;\n"; + pr " }\n"; + ) test_names; pr "\n"; + pr " guestfs_close (g);\n"; + pr " snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir);\n"; + pr " unlink (buf);\n"; + pr " snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir);\n"; + pr " unlink (buf);\n"; + pr " snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir);\n"; + pr " unlink (buf);\n"; + pr "\n"; + pr " if (failed > 0) {\n"; + pr " printf (\"***** %%d / %d tests FAILED *****\\n\", failed);\n" + nr_tests; + pr " exit (1);\n"; + pr " }\n"; + pr "\n"; - pr "int main (int argc, char *argv[])\n"; - pr "{\n"; pr " exit (0);\n"; pr "}\n" +and generate_one_test name i (init, test) = + let test_name = sprintf "test_%s_%d" name i in + + pr "static int %s (void)\n" test_name; + pr "{\n"; + + (match init with + | InitNone -> + pr " /* InitNone for %s (%d) */\n" name i; + List.iter (generate_test_command_call test_name) + [["umount_all"]; + ["lvm_remove_all"]] + | InitEmpty -> + pr " /* InitEmpty for %s (%d): create ext2 on /dev/sda1 */\n" name i; + List.iter (generate_test_command_call test_name) + [["umount_all"]; + ["lvm_remove_all"]; + ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["mkfs"; "ext2"; "/dev/sda1"]; + ["mount"; "/dev/sda1"; "/"]] + | InitEmptyLVM -> + pr " /* InitEmptyLVM for %s (%d): create ext2 on /dev/VG/LV */\n" + name i; + List.iter (generate_test_command_call test_name) + [["umount_all"]; + ["lvm_remove_all"]; + ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV"; "VG"; "8"]; + ["mkfs"; "ext2"; "/dev/VG/LV"]; + ["mount"; "/dev/VG/LV"; "/"]] + ); + + let get_seq_last = function + | [] -> + failwithf "%s: you cannot use [] (empty list) when expecting a command" + test_name + | seq -> + let seq = List.rev seq in + List.rev (List.tl seq), List.hd seq + in + + (match test with + | TestRun seq -> + pr " /* TestRun for %s (%d) */\n" name i; + List.iter (generate_test_command_call test_name) seq + | TestOutput (seq, expected) -> + pr " /* TestOutput for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (strcmp (r, \"%s\") != 0) {\n" (c_quote expected); + pr " fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r);\n" test_name (c_quote expected); + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputList (seq, expected) -> + pr " /* TestOutputList for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + iteri ( + fun i str -> + pr " if (!r[%d]) {\n" i; + pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n"; + pr " if (strcmp (r[%d], \"%s\") != 0) {\n" i (c_quote str); + pr " fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r[%d]);\n" test_name (c_quote str) i; + pr " return -1;\n"; + pr " }\n" + ) expected; + pr " if (r[%d] != NULL) {\n" (List.length expected); + pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n" + test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputInt (seq, expected) -> + pr " /* TestOutputInt for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (r != %d) {\n" expected; + pr " fprintf (stderr, \"%s: expected %d but got %%d\\n\", r);\n" + test_name expected; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputTrue seq -> + pr " /* TestOutputTrue for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (!r) {\n"; + pr " fprintf (stderr, \"%s: expected true, got false\\n\");\n" + test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputFalse seq -> + pr " /* TestOutputFalse for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (r) {\n"; + pr " fprintf (stderr, \"%s: expected false, got true\\n\");\n" + test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputLength (seq, expected) -> + pr " /* TestOutputLength for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " int j;\n"; + pr " for (j = 0; j < %d; ++j)\n" expected; + pr " if (r[j] == NULL) {\n"; + pr " fprintf (stderr, \"%s: short list returned\\n\");\n" + test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n"; + pr " if (r[j] != NULL) {\n"; + pr " fprintf (stderr, \"%s: long list returned\\n\");\n" + test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestLastFail seq -> + pr " /* TestLastFail for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call test_name ~expect_error:true last + ); + + pr " return 0;\n"; + pr "}\n"; + pr "\n"; + test_name + +(* Generate the code to run a command, leaving the result in 'r'. + * If you expect to get an error then you should set expect_error:true. + *) +and generate_test_command_call ?(expect_error = false) ?test test_name cmd = + match cmd with + | [] -> assert false + | name :: args -> + (* Look up the command to find out what args/ret it has. *) + let style = + try + let _, style, _, _, _, _, _ = + List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in + style + with Not_found -> + failwithf "%s: in test, command %s was not found" test_name name in + + if List.length (snd style) <> List.length args then + failwithf "%s: in test, wrong number of args given to %s" + test_name name; + + pr " {\n"; + + List.iter ( + function + | String _, _ + | OptString _, _ + | Int _, _ + | Bool _, _ -> () + | StringList n, arg -> + pr " char *%s[] = {\n" n; + let strs = string_split " " arg in + List.iter ( + fun str -> pr " \"%s\",\n" (c_quote str) + ) strs; + pr " NULL\n"; + pr " };\n"; + ) (List.combine (snd style) args); + + let error_code = + match fst style with + | RErr | RInt _ | RBool _ -> pr " int r;\n"; "-1" + | RConstString _ -> pr " const char *r;\n"; "NULL" + | RString _ -> pr " char *r;\n"; "NULL" + | RStringList _ -> + pr " char **r;\n"; + pr " int i;\n"; + "NULL" + | RIntBool _ -> + pr " struct guestfs_int_bool *r;\n"; + "NULL" + | RPVList _ -> + pr " struct guestfs_lvm_pv_list *r;\n"; + "NULL" + | RVGList _ -> + pr " struct guestfs_lvm_vg_list *r;\n"; + "NULL" + | RLVList _ -> + pr " struct guestfs_lvm_lv_list *r;\n"; + "NULL" in + + pr " suppress_error = %d;\n" (if expect_error then 1 else 0); + pr " r = guestfs_%s (g" name; + + (* Generate the parameters. *) + List.iter ( + function + | String _, arg -> pr ", \"%s\"" (c_quote arg) + | OptString _, arg -> + if arg = "NULL" then pr ", NULL" else pr ", \"%s\"" (c_quote arg) + | StringList n, _ -> + pr ", %s" n + | Int _, arg -> + let i = + try int_of_string arg + with Failure "int_of_string" -> + failwithf "%s: expecting an int, but got '%s'" test_name arg in + pr ", %d" i + | Bool _, arg -> + let b = bool_of_string arg in pr ", %d" (if b then 1 else 0) + ) (List.combine (snd style) args); + + pr ");\n"; + if not expect_error then + pr " if (r == %s)\n" error_code + else + pr " if (r != %s)\n" error_code; + pr " return -1;\n"; + + (* Insert the test code. *) + (match test with + | None -> () + | Some f -> f () + ); + + (match fst style with + | RErr | RInt _ | RBool _ | RConstString _ -> () + | RString _ -> pr " free (r);\n" + | RStringList _ -> + pr " for (i = 0; r[i] != NULL; ++i)\n"; + pr " free (r[i]);\n"; + pr " free (r);\n" + | RIntBool _ -> + pr " guestfs_free_int_bool (r);\n" + | RPVList _ -> + pr " guestfs_free_lvm_pv_list (r);\n" + | RVGList _ -> + pr " guestfs_free_lvm_vg_list (r);\n" + | RLVList _ -> + pr " guestfs_free_lvm_lv_list (r);\n" + ); + + pr " }\n" + +and c_quote str = + let str = replace_str str "\r" "\\r" in + let str = replace_str str "\n" "\\n" in + let str = replace_str str "\t" "\\t" in + str + (* Generate a lot of different functions for guestfish. *) and generate_fish_cmds () = generate_header CStyle GPLv2; @@ -1893,11 +2552,19 @@ and generate_fish_cmds () = let warnings = if List.mem ProtocolLimitWarning flags then - "\n\nBecause of the message protocol, there is a transfer limit -of somewhere between 2MB and 4MB. To transfer large files you should use -FTP." + ("\n\n" ^ protocol_limit_warning) else "" in + (* For DangerWillRobinson commands, we should probably have + * guestfish prompt before allowing you to use them (especially + * in interactive mode). XXX + *) + let warnings = + warnings ^ + if List.mem DangerWillRobinson flags then + ("\n\n" ^ danger_will_robinson) + else "" in + let describe_alias = if name <> alias then sprintf "\n\nYou can use '%s' as an alias for this command." alias @@ -1977,8 +2644,9 @@ FTP." ); List.iter ( function - | String n -> pr " const char *%s;\n" n + | String n | OptString n -> pr " const char *%s;\n" n + | StringList n -> pr " char **%s;\n" n | Bool n -> pr " int %s;\n" n | Int n -> pr " int %s;\n" n ) (snd style); @@ -1998,6 +2666,8 @@ FTP." | OptString name -> pr " %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n" name i i + | StringList name -> + pr " %s = parse_string_list (argv[%d]);\n" name i | Bool name -> pr " %s = is_true (argv[%d]) ? 1 : 0;\n" name i | Int name -> @@ -2115,12 +2785,19 @@ and generate_fish_actions_pod () = function | String n -> pr " %s" n | OptString n -> pr " %s" n + | StringList n -> pr " %s,..." n | Bool _ -> pr " true|false" | Int n -> pr " %s" n ) (snd style); pr "\n"; pr "\n"; - pr "%s\n\n" longdesc + pr "%s\n\n" longdesc; + + 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 (* Generate a C function prototype. *) @@ -2169,6 +2846,7 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) function | String n -> next (); pr "const char *%s" n | OptString n -> next (); pr "const char *%s" n + | StringList n -> next (); pr "char * const* const %s" n | Bool n -> next (); pr "int %s" n | Int n -> next (); pr "int %s" n ) (snd style); @@ -2190,9 +2868,10 @@ and generate_call_args ?handle style = if !comma then pr ", "; comma := true; match arg with - | String n -> pr "%s" n - | OptString n -> pr "%s" n - | Bool n -> pr "%s" n + | String n + | OptString n + | StringList n + | Bool n | Int n -> pr "%s" n ) (snd style); pr ")" @@ -2339,18 +3018,23 @@ and generate_ocaml_c () = List.iter ( fun (name, style, _, _, _, _, _) -> + let params = + "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in + pr "CAMLprim value\n"; - pr "ocaml_guestfs_%s (value gv" name; - List.iter ( - fun arg -> pr ", value %sv" (name_of_argt arg) - ) (snd style); + pr "ocaml_guestfs_%s (value %s" name (List.hd params); + List.iter (pr ", value %s") (List.tl params); pr ")\n"; pr "{\n"; - pr " CAMLparam%d (gv" (1 + (List.length (snd style))); - List.iter ( - fun arg -> pr ", %sv" (name_of_argt arg) - ) (snd style); - pr ");\n"; + + (match params with + | p1 :: p2 :: p3 :: p4 :: p5 :: rest -> + pr " CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]); + pr " CAMLxparam%d (%s);\n" + (List.length rest) (String.concat ", " rest) + | ps -> + pr " CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps) + ); pr " CAMLlocal1 (rv);\n"; pr "\n"; @@ -2367,6 +3051,8 @@ and generate_ocaml_c () = pr " const char *%s =\n" n; pr " %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n" n n + | StringList n -> + pr " char **%s = ocaml_guestfs_strings_val (%sv);\n" n n | Bool n -> pr " int %s = Bool_val (%sv);\n" n n | Int n -> @@ -2402,6 +3088,14 @@ and generate_ocaml_c () = generate_call_args ~handle:"g" style; pr ";\n"; pr " caml_leave_blocking_section ();\n"; + + List.iter ( + function + | StringList n -> + pr " ocaml_guestfs_free_strings (%s);\n" n; + | String _ | OptString _ | Bool _ | Int _ -> () + ) (snd style); + pr " if (r == %s)\n" error_code; pr " ocaml_guestfs_raise_error (g, \"%s\");\n" name; pr "\n"; @@ -2436,7 +3130,18 @@ and generate_ocaml_c () = pr " CAMLreturn (rv);\n"; pr "}\n"; - pr "\n" + pr "\n"; + + if List.length params > 5 then ( + pr "CAMLprim value\n"; + pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name; + pr "{\n"; + pr " return ocaml_guestfs_%s (argv[0]" name; + iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params); + pr ");\n"; + pr "}\n"; + pr "\n" + ) ) all_functions and generate_ocaml_lvm_structure_decls () = @@ -2462,6 +3167,7 @@ and generate_ocaml_prototype ?(is_external = false) name style = function | String _ -> pr "string -> " | OptString _ -> pr "string option -> " + | StringList _ -> pr "string array -> " | Bool _ -> pr "bool -> " | Int _ -> pr "int -> " ) (snd style); @@ -2477,7 +3183,12 @@ and generate_ocaml_prototype ?(is_external = false) name style = | RVGList _ -> pr "lvm_vg array" | RLVList _ -> pr "lvm_lv array" ); - if is_external then pr " = \"ocaml_guestfs_%s\"" name; + if is_external then ( + pr " = "; + if List.length (snd style) + 1 > 5 then + pr "\"ocaml_guestfs_%s_byte\" " name; + pr "\"ocaml_guestfs_%s\"" name + ); pr "\n" (* Generate Perl xs code, a sort of crazy variation of C with macros. *) @@ -2539,6 +3250,35 @@ error_handler (guestfs_h *g, last_error = strdup (msg); } +/* http://www.perlmonks.org/?node_id=680842 */ +static char ** +XS_unpack_charPtrPtr (SV *arg) { + char **ret; + AV *av; + I32 i; + + if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV) { + croak (\"array reference expected\"); + } + + av = (AV *)SvRV (arg); + ret = (char **)malloc (av_len (av) + 1 + 1); + + for (i = 0; i <= av_len (av); i++) { + SV **elem = av_fetch (av, i, 0); + + if (!elem || !*elem) { + croak (\"missing element in list\"); + } + + ret[i] = SvPV_nolen (*elem); + } + + ret[i + 1] = NULL; + + return ret; +} + MODULE = Sys::Guestfs PACKAGE = Sys::Guestfs guestfs_h * @@ -2581,17 +3321,32 @@ DESTROY (g) function | String n -> pr " char *%s;\n" n | OptString n -> pr " char *%s;\n" n + | StringList n -> pr " char **%s;\n" n | Bool n -> pr " int %s;\n" n | Int n -> pr " int %s;\n" n ) (snd style); + + let do_cleanups () = + List.iter ( + function + | String _ + | OptString _ + | Bool _ + | Int _ -> () + | StringList n -> pr " free (%s);\n" n + ) (snd style) + in + (* Code. *) (match fst style with | RErr -> pr " PPCODE:\n"; pr " if (guestfs_%s " name; generate_call_args ~handle:"g" style; - pr " == -1)\n"; - pr " croak (\"%s: %%s\", last_error);\n" name + pr " == -1) {\n"; + do_cleanups (); + pr " croak (\"%s: %%s\", last_error);\n" name; + pr " }\n" | RInt n | RBool n -> pr "PREINIT:\n"; @@ -2600,8 +3355,10 @@ DESTROY (g) pr " %s = guestfs_%s " n name; generate_call_args ~handle:"g" style; pr ";\n"; - pr " if (%s == -1)\n" n; + pr " if (%s == -1) {\n" n; + do_cleanups (); pr " croak (\"%s: %%s\", last_error);\n" name; + pr " }\n"; pr " RETVAL = newSViv (%s);\n" n; pr " OUTPUT:\n"; pr " RETVAL\n" @@ -2612,8 +3369,10 @@ DESTROY (g) pr " %s = guestfs_%s " n name; generate_call_args ~handle:"g" style; pr ";\n"; - pr " if (%s == NULL)\n" n; + pr " if (%s == NULL) {\n" n; + do_cleanups (); pr " croak (\"%s: %%s\", last_error);\n" name; + pr " }\n"; pr " RETVAL = newSVpv (%s, 0);\n" n; pr " OUTPUT:\n"; pr " RETVAL\n" @@ -2624,8 +3383,10 @@ DESTROY (g) pr " %s = guestfs_%s " n name; generate_call_args ~handle:"g" style; pr ";\n"; - pr " if (%s == NULL)\n" n; + pr " if (%s == NULL) {\n" n; + do_cleanups (); pr " croak (\"%s: %%s\", last_error);\n" name; + pr " }\n"; pr " RETVAL = newSVpv (%s, 0);\n" n; pr " free (%s);\n" n; pr " OUTPUT:\n"; @@ -2638,8 +3399,10 @@ DESTROY (g) pr " %s = guestfs_%s " n name; generate_call_args ~handle:"g" style; pr ";\n"; - pr " if (%s == NULL)\n" n; + pr " if (%s == NULL) {\n" n; + do_cleanups (); pr " croak (\"%s: %%s\", last_error);\n" name; + pr " }\n"; pr " for (n = 0; %s[n] != NULL; ++n) /**/;\n" n; pr " EXTEND (SP, n);\n"; pr " for (i = 0; i < n; ++i) {\n"; @@ -2654,8 +3417,10 @@ DESTROY (g) pr " r = guestfs_%s " name; generate_call_args ~handle:"g" style; pr ";\n"; - pr " if (r == NULL)\n"; + pr " if (r == NULL) {\n"; + do_cleanups (); pr " croak (\"%s: %%s\", last_error);\n" name; + pr " }\n"; pr " EXTEND (SP, 2);\n"; pr " PUSHs (sv_2mortal (newSViv (r->i)));\n"; pr " PUSHs (sv_2mortal (newSViv (r->b)));\n"; @@ -2667,6 +3432,9 @@ DESTROY (g) | RLVList n -> generate_perl_lvm_code "lv" lv_cols name style n; ); + + do_cleanups (); + pr "\n" ) all_functions @@ -2797,9 +3565,9 @@ sub new { pr "\n\n"; pr "%s\n\n" longdesc; if List.mem ProtocolLimitWarning flags then - pr "Because of the message protocol, there is a transfer limit -of somewhere between 2MB and 4MB. To transfer large files you should use -FTP.\n\n"; + pr "%s\n\n" protocol_limit_warning; + if List.mem DangerWillRobinson flags then + pr "%s\n\n" danger_will_robinson ) all_functions_sorted; (* End of file. *) @@ -2844,7 +3612,11 @@ and generate_perl_prototype name style = fun arg -> if !comma then pr ", "; comma := true; - pr "%s" (name_of_argt arg) + match arg with + | String n | OptString n | Bool n | Int n -> + pr "$%s" n + | StringList n -> + pr "\\@%s" n ) (snd style); pr ");" diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index 25892cd0..669ca767 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -2540,3 +2540,934 @@ int guestfs_chown (guestfs_h *g, return 0; } +struct exists_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_exists_ret ret; +}; + +static void exists_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct exists_rv *rv = (struct exists_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_exists: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_exists: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_exists_ret (xdr, &rv->ret)) { + error (g, "guestfs_exists: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_exists (guestfs_h *g, + const char *path) +{ + struct guestfs_exists_args args; + struct exists_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_exists called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + serial = dispatch (g, GUESTFS_PROC_EXISTS, + (xdrproc_t) xdr_guestfs_exists_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = exists_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_exists failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_EXISTS, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return rv.ret.existsflag; +} + +struct is_file_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_is_file_ret ret; +}; + +static void is_file_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct is_file_rv *rv = (struct is_file_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_is_file: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_is_file: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_is_file_ret (xdr, &rv->ret)) { + error (g, "guestfs_is_file: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_is_file (guestfs_h *g, + const char *path) +{ + struct guestfs_is_file_args args; + struct is_file_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_is_file called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + serial = dispatch (g, GUESTFS_PROC_IS_FILE, + (xdrproc_t) xdr_guestfs_is_file_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = is_file_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_is_file failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_IS_FILE, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return rv.ret.fileflag; +} + +struct is_dir_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_is_dir_ret ret; +}; + +static void is_dir_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct is_dir_rv *rv = (struct is_dir_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_is_dir: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_is_dir: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_is_dir_ret (xdr, &rv->ret)) { + error (g, "guestfs_is_dir: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_is_dir (guestfs_h *g, + const char *path) +{ + struct guestfs_is_dir_args args; + struct is_dir_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_is_dir called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + serial = dispatch (g, GUESTFS_PROC_IS_DIR, + (xdrproc_t) xdr_guestfs_is_dir_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = is_dir_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_is_dir failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_IS_DIR, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return rv.ret.dirflag; +} + +struct pvcreate_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void pvcreate_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct pvcreate_rv *rv = (struct pvcreate_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_pvcreate: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_pvcreate: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_pvcreate (guestfs_h *g, + const char *device) +{ + struct guestfs_pvcreate_args args; + struct pvcreate_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_pvcreate called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.device = (char *) device; + serial = dispatch (g, GUESTFS_PROC_PVCREATE, + (xdrproc_t) xdr_guestfs_pvcreate_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = pvcreate_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_pvcreate failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_PVCREATE, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct vgcreate_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void vgcreate_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct vgcreate_rv *rv = (struct vgcreate_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_vgcreate: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_vgcreate: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_vgcreate (guestfs_h *g, + const char *volgroup, + char * const* const physvols) +{ + struct guestfs_vgcreate_args args; + struct vgcreate_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_vgcreate called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.volgroup = (char *) volgroup; + args.physvols.physvols_val = (char **) physvols; + for (args.physvols.physvols_len = 0; physvols[args.physvols.physvols_len]; args.physvols.physvols_len++) ; + serial = dispatch (g, GUESTFS_PROC_VGCREATE, + (xdrproc_t) xdr_guestfs_vgcreate_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = vgcreate_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_vgcreate failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_VGCREATE, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct lvcreate_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void lvcreate_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct lvcreate_rv *rv = (struct lvcreate_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_lvcreate: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_lvcreate: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_lvcreate (guestfs_h *g, + const char *logvol, + const char *volgroup, + int mbytes) +{ + struct guestfs_lvcreate_args args; + struct lvcreate_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_lvcreate called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.logvol = (char *) logvol; + args.volgroup = (char *) volgroup; + args.mbytes = mbytes; + serial = dispatch (g, GUESTFS_PROC_LVCREATE, + (xdrproc_t) xdr_guestfs_lvcreate_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = lvcreate_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_lvcreate failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LVCREATE, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct mkfs_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void mkfs_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct mkfs_rv *rv = (struct mkfs_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_mkfs: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_mkfs: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_mkfs (guestfs_h *g, + const char *fstype, + const char *device) +{ + struct guestfs_mkfs_args args; + struct mkfs_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_mkfs called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.fstype = (char *) fstype; + args.device = (char *) device; + serial = dispatch (g, GUESTFS_PROC_MKFS, + (xdrproc_t) xdr_guestfs_mkfs_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = mkfs_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_mkfs failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_MKFS, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct sfdisk_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void sfdisk_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct sfdisk_rv *rv = (struct sfdisk_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_sfdisk: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_sfdisk: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_sfdisk (guestfs_h *g, + const char *device, + int cyls, + int heads, + int sectors, + char * const* const lines) +{ + struct guestfs_sfdisk_args args; + struct sfdisk_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_sfdisk called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.device = (char *) device; + args.cyls = cyls; + args.heads = heads; + args.sectors = sectors; + args.lines.lines_val = (char **) lines; + for (args.lines.lines_len = 0; lines[args.lines.lines_len]; args.lines.lines_len++) ; + serial = dispatch (g, GUESTFS_PROC_SFDISK, + (xdrproc_t) xdr_guestfs_sfdisk_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = sfdisk_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_sfdisk failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_SFDISK, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct write_file_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void write_file_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct write_file_rv *rv = (struct write_file_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_write_file: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_write_file: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_write_file (guestfs_h *g, + const char *path, + const char *content, + int size) +{ + struct guestfs_write_file_args args; + struct write_file_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_write_file called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + args.content = (char *) content; + args.size = size; + serial = dispatch (g, GUESTFS_PROC_WRITE_FILE, + (xdrproc_t) xdr_guestfs_write_file_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = write_file_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_write_file failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_WRITE_FILE, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct umount_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void umount_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct umount_rv *rv = (struct umount_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_umount: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_umount: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_umount (guestfs_h *g, + const char *pathordevice) +{ + struct guestfs_umount_args args; + struct umount_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_umount called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.pathordevice = (char *) pathordevice; + serial = dispatch (g, GUESTFS_PROC_UMOUNT, + (xdrproc_t) xdr_guestfs_umount_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = umount_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_umount failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_UMOUNT, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct mounts_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_mounts_ret ret; +}; + +static void mounts_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct mounts_rv *rv = (struct mounts_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_mounts: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_mounts: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_mounts_ret (xdr, &rv->ret)) { + error (g, "guestfs_mounts: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +char **guestfs_mounts (guestfs_h *g) +{ + struct mounts_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_mounts called from the wrong state, %d != READY", + g->state); + return NULL; + } + + memset (&rv, 0, sizeof rv); + + serial = dispatch (g, GUESTFS_PROC_MOUNTS, NULL, NULL); + if (serial == -1) + return NULL; + + rv.cb_done = 0; + g->reply_cb_internal = mounts_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_mounts failed, see earlier error messages"); + return NULL; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_MOUNTS, serial) == -1) + return NULL; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return NULL; + } + + /* caller will free this, but we need to add a NULL entry */ + rv.ret.devices.devices_val = safe_realloc (g, rv.ret.devices.devices_val, + sizeof (char *) * (rv.ret.devices.devices_len + 1)); + rv.ret.devices.devices_val[rv.ret.devices.devices_len] = NULL; + return rv.ret.devices.devices_val; +} + +struct umount_all_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void umount_all_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct umount_all_rv *rv = (struct umount_all_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_umount_all: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_umount_all: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_umount_all (guestfs_h *g) +{ + struct umount_all_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_umount_all called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + serial = dispatch (g, GUESTFS_PROC_UMOUNT_ALL, NULL, NULL); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = umount_all_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_umount_all failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_UMOUNT_ALL, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct lvm_remove_all_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void lvm_remove_all_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct lvm_remove_all_rv *rv = (struct lvm_remove_all_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_lvm_remove_all: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_lvm_remove_all: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_lvm_remove_all (guestfs_h *g) +{ + struct lvm_remove_all_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_lvm_remove_all called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + serial = dispatch (g, GUESTFS_PROC_LVM_REMOVE_ALL, NULL, NULL); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = lvm_remove_all_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_lvm_remove_all failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LVM_REMOVE_ALL, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index 06f1485b..d40ef150 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -66,3 +66,16 @@ extern int guestfs_mkdir (guestfs_h *handle, const char *path); extern int guestfs_mkdir_p (guestfs_h *handle, const char *path); extern int guestfs_chmod (guestfs_h *handle, int mode, const char *path); extern int guestfs_chown (guestfs_h *handle, int owner, int group, const char *path); +extern int guestfs_exists (guestfs_h *handle, const char *path); +extern int guestfs_is_file (guestfs_h *handle, const char *path); +extern int guestfs_is_dir (guestfs_h *handle, const char *path); +extern int guestfs_pvcreate (guestfs_h *handle, const char *device); +extern int guestfs_vgcreate (guestfs_h *handle, const char *volgroup, char * const* const physvols); +extern int guestfs_lvcreate (guestfs_h *handle, const char *logvol, const char *volgroup, int mbytes); +extern int guestfs_mkfs (guestfs_h *handle, const char *fstype, const char *device); +extern int guestfs_sfdisk (guestfs_h *handle, const char *device, int cyls, int heads, int sectors, char * const* const lines); +extern int guestfs_write_file (guestfs_h *handle, const char *path, const char *content, int size); +extern int guestfs_umount (guestfs_h *handle, const char *pathordevice); +extern char **guestfs_mounts (guestfs_h *handle); +extern int guestfs_umount_all (guestfs_h *handle); +extern int guestfs_lvm_remove_all (guestfs_h *handle); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index 781c2daf..8e868126 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -618,6 +618,214 @@ xdr_guestfs_chown_args (XDR *xdrs, guestfs_chown_args *objp) return TRUE; } +bool_t +xdr_guestfs_exists_args (XDR *xdrs, guestfs_exists_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_exists_ret (XDR *xdrs, guestfs_exists_ret *objp) +{ + register int32_t *buf; + + if (!xdr_bool (xdrs, &objp->existsflag)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_is_file_args (XDR *xdrs, guestfs_is_file_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_is_file_ret (XDR *xdrs, guestfs_is_file_ret *objp) +{ + register int32_t *buf; + + if (!xdr_bool (xdrs, &objp->fileflag)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_is_dir_args (XDR *xdrs, guestfs_is_dir_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_is_dir_ret (XDR *xdrs, guestfs_is_dir_ret *objp) +{ + register int32_t *buf; + + if (!xdr_bool (xdrs, &objp->dirflag)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_pvcreate_args (XDR *xdrs, guestfs_pvcreate_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->device, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_vgcreate_args (XDR *xdrs, guestfs_vgcreate_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->volgroup, ~0)) + return FALSE; + if (!xdr_array (xdrs, (char **)&objp->physvols.physvols_val, (u_int *) &objp->physvols.physvols_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_lvcreate_args (XDR *xdrs, guestfs_lvcreate_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->logvol, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->volgroup, ~0)) + return FALSE; + if (!xdr_int (xdrs, &objp->mbytes)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_mkfs_args (XDR *xdrs, guestfs_mkfs_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->fstype, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->device, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_sfdisk_args (XDR *xdrs, guestfs_sfdisk_args *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_string (xdrs, &objp->device, ~0)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->cyls)) + return FALSE; + if (!xdr_int (xdrs, &objp->heads)) + return FALSE; + if (!xdr_int (xdrs, &objp->sectors)) + return FALSE; + + } else { + IXDR_PUT_LONG(buf, objp->cyls); + IXDR_PUT_LONG(buf, objp->heads); + IXDR_PUT_LONG(buf, objp->sectors); + } + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + if (!xdr_string (xdrs, &objp->device, ~0)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->cyls)) + return FALSE; + if (!xdr_int (xdrs, &objp->heads)) + return FALSE; + if (!xdr_int (xdrs, &objp->sectors)) + return FALSE; + + } else { + objp->cyls = IXDR_GET_LONG(buf); + objp->heads = IXDR_GET_LONG(buf); + objp->sectors = IXDR_GET_LONG(buf); + } + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; + } + + if (!xdr_string (xdrs, &objp->device, ~0)) + return FALSE; + if (!xdr_int (xdrs, &objp->cyls)) + return FALSE; + if (!xdr_int (xdrs, &objp->heads)) + return FALSE; + if (!xdr_int (xdrs, &objp->sectors)) + return FALSE; + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_write_file_args (XDR *xdrs, guestfs_write_file_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->content, ~0)) + return FALSE; + if (!xdr_int (xdrs, &objp->size)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_umount_args (XDR *xdrs, guestfs_umount_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->pathordevice, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_mounts_ret (XDR *xdrs, guestfs_mounts_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->devices.devices_val, (u_int *) &objp->devices.devices_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 508bc4e3..fedd0074 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -337,6 +337,95 @@ struct guestfs_chown_args { }; typedef struct guestfs_chown_args guestfs_chown_args; +struct guestfs_exists_args { + char *path; +}; +typedef struct guestfs_exists_args guestfs_exists_args; + +struct guestfs_exists_ret { + bool_t existsflag; +}; +typedef struct guestfs_exists_ret guestfs_exists_ret; + +struct guestfs_is_file_args { + char *path; +}; +typedef struct guestfs_is_file_args guestfs_is_file_args; + +struct guestfs_is_file_ret { + bool_t fileflag; +}; +typedef struct guestfs_is_file_ret guestfs_is_file_ret; + +struct guestfs_is_dir_args { + char *path; +}; +typedef struct guestfs_is_dir_args guestfs_is_dir_args; + +struct guestfs_is_dir_ret { + bool_t dirflag; +}; +typedef struct guestfs_is_dir_ret guestfs_is_dir_ret; + +struct guestfs_pvcreate_args { + char *device; +}; +typedef struct guestfs_pvcreate_args guestfs_pvcreate_args; + +struct guestfs_vgcreate_args { + char *volgroup; + struct { + u_int physvols_len; + str *physvols_val; + } physvols; +}; +typedef struct guestfs_vgcreate_args guestfs_vgcreate_args; + +struct guestfs_lvcreate_args { + char *logvol; + char *volgroup; + int mbytes; +}; +typedef struct guestfs_lvcreate_args guestfs_lvcreate_args; + +struct guestfs_mkfs_args { + char *fstype; + char *device; +}; +typedef struct guestfs_mkfs_args guestfs_mkfs_args; + +struct guestfs_sfdisk_args { + char *device; + int cyls; + int heads; + int sectors; + struct { + u_int lines_len; + str *lines_val; + } lines; +}; +typedef struct guestfs_sfdisk_args guestfs_sfdisk_args; + +struct guestfs_write_file_args { + char *path; + char *content; + int size; +}; +typedef struct guestfs_write_file_args guestfs_write_file_args; + +struct guestfs_umount_args { + char *pathordevice; +}; +typedef struct guestfs_umount_args guestfs_umount_args; + +struct guestfs_mounts_ret { + struct { + u_int devices_len; + str *devices_val; + } devices; +}; +typedef struct guestfs_mounts_ret guestfs_mounts_ret; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -373,7 +462,20 @@ enum guestfs_procedure { GUESTFS_PROC_MKDIR_P = 33, GUESTFS_PROC_CHMOD = 34, GUESTFS_PROC_CHOWN = 35, - GUESTFS_PROC_dummy = 35 + 1, + GUESTFS_PROC_EXISTS = 36, + GUESTFS_PROC_IS_FILE = 37, + GUESTFS_PROC_IS_DIR = 38, + GUESTFS_PROC_PVCREATE = 39, + GUESTFS_PROC_VGCREATE = 40, + GUESTFS_PROC_LVCREATE = 41, + GUESTFS_PROC_MKFS = 42, + GUESTFS_PROC_SFDISK = 43, + GUESTFS_PROC_WRITE_FILE = 44, + GUESTFS_PROC_UMOUNT = 45, + GUESTFS_PROC_MOUNTS = 46, + GUESTFS_PROC_UMOUNT_ALL = 47, + GUESTFS_PROC_LVM_REMOVE_ALL = 48, + GUESTFS_PROC_dummy = 48 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -459,6 +561,20 @@ extern bool_t xdr_guestfs_mkdir_args (XDR *, guestfs_mkdir_args*); extern bool_t xdr_guestfs_mkdir_p_args (XDR *, guestfs_mkdir_p_args*); extern bool_t xdr_guestfs_chmod_args (XDR *, guestfs_chmod_args*); extern bool_t xdr_guestfs_chown_args (XDR *, guestfs_chown_args*); +extern bool_t xdr_guestfs_exists_args (XDR *, guestfs_exists_args*); +extern bool_t xdr_guestfs_exists_ret (XDR *, guestfs_exists_ret*); +extern bool_t xdr_guestfs_is_file_args (XDR *, guestfs_is_file_args*); +extern bool_t xdr_guestfs_is_file_ret (XDR *, guestfs_is_file_ret*); +extern bool_t xdr_guestfs_is_dir_args (XDR *, guestfs_is_dir_args*); +extern bool_t xdr_guestfs_is_dir_ret (XDR *, guestfs_is_dir_ret*); +extern bool_t xdr_guestfs_pvcreate_args (XDR *, guestfs_pvcreate_args*); +extern bool_t xdr_guestfs_vgcreate_args (XDR *, guestfs_vgcreate_args*); +extern bool_t xdr_guestfs_lvcreate_args (XDR *, guestfs_lvcreate_args*); +extern bool_t xdr_guestfs_mkfs_args (XDR *, guestfs_mkfs_args*); +extern bool_t xdr_guestfs_sfdisk_args (XDR *, guestfs_sfdisk_args*); +extern bool_t xdr_guestfs_write_file_args (XDR *, guestfs_write_file_args*); +extern bool_t xdr_guestfs_umount_args (XDR *, guestfs_umount_args*); +extern bool_t xdr_guestfs_mounts_ret (XDR *, guestfs_mounts_ret*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -514,6 +630,20 @@ extern bool_t xdr_guestfs_mkdir_args (); extern bool_t xdr_guestfs_mkdir_p_args (); extern bool_t xdr_guestfs_chmod_args (); extern bool_t xdr_guestfs_chown_args (); +extern bool_t xdr_guestfs_exists_args (); +extern bool_t xdr_guestfs_exists_ret (); +extern bool_t xdr_guestfs_is_file_args (); +extern bool_t xdr_guestfs_is_file_ret (); +extern bool_t xdr_guestfs_is_dir_args (); +extern bool_t xdr_guestfs_is_dir_ret (); +extern bool_t xdr_guestfs_pvcreate_args (); +extern bool_t xdr_guestfs_vgcreate_args (); +extern bool_t xdr_guestfs_lvcreate_args (); +extern bool_t xdr_guestfs_mkfs_args (); +extern bool_t xdr_guestfs_sfdisk_args (); +extern bool_t xdr_guestfs_write_file_args (); +extern bool_t xdr_guestfs_umount_args (); +extern bool_t xdr_guestfs_mounts_ret (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index 41116b53..c547d0b3 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -262,6 +262,72 @@ struct guestfs_chown_args { string path<>; }; +struct guestfs_exists_args { + string path<>; +}; + +struct guestfs_exists_ret { + bool existsflag; +}; + +struct guestfs_is_file_args { + string path<>; +}; + +struct guestfs_is_file_ret { + bool fileflag; +}; + +struct guestfs_is_dir_args { + string path<>; +}; + +struct guestfs_is_dir_ret { + bool dirflag; +}; + +struct guestfs_pvcreate_args { + string device<>; +}; + +struct guestfs_vgcreate_args { + string volgroup<>; + str physvols<>; +}; + +struct guestfs_lvcreate_args { + string logvol<>; + string volgroup<>; + int mbytes; +}; + +struct guestfs_mkfs_args { + string fstype<>; + string device<>; +}; + +struct guestfs_sfdisk_args { + string device<>; + int cyls; + int heads; + int sectors; + str lines<>; +}; + +struct guestfs_write_file_args { + string path<>; + string content<>; + int size; +}; + +struct guestfs_umount_args { + string pathordevice<>; +}; + +struct guestfs_mounts_ret { + str devices<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -298,6 +364,19 @@ enum guestfs_procedure { GUESTFS_PROC_MKDIR_P = 33, GUESTFS_PROC_CHMOD = 34, GUESTFS_PROC_CHOWN = 35, + GUESTFS_PROC_EXISTS = 36, + GUESTFS_PROC_IS_FILE = 37, + GUESTFS_PROC_IS_DIR = 38, + GUESTFS_PROC_PVCREATE = 39, + GUESTFS_PROC_VGCREATE = 40, + GUESTFS_PROC_LVCREATE = 41, + GUESTFS_PROC_MKFS = 42, + GUESTFS_PROC_SFDISK = 43, + GUESTFS_PROC_WRITE_FILE = 44, + GUESTFS_PROC_UMOUNT = 45, + GUESTFS_PROC_MOUNTS = 46, + GUESTFS_PROC_UMOUNT_ALL = 47, + GUESTFS_PROC_LVM_REMOVE_ALL = 48, GUESTFS_PROC_dummy }; -- cgit