generator: Make 'xz' into an optional group.
[libguestfs.git] / src / generator.ml
index 090c280..d4ef81a 100755 (executable)
@@ -575,7 +575,7 @@ The first character of C<param> string must be a C<-> (dash).
 
 C<value> can be NULL.");
 
-  ("set_qemu", (RErr, [String "qemu"]), -1, [FishAlias "qemu"],
+  ("set_qemu", (RErr, [OptString "qemu"]), -1, [FishAlias "qemu"],
    [],
    "set the qemu binary",
    "\
@@ -607,7 +607,7 @@ 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_path", (RErr, [OptString "searchpath"]), -1, [FishAlias "path"],
    [],
    "set the search path",
    "\
@@ -938,7 +938,7 @@ let daemon_functions = [
       [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
-       ["write_file"; "/new"; "new file contents"; "0"];
+       ["write"; "/new"; "new file contents"];
        ["cat"; "/new"]], "new file contents")],
    "mount a guest disk at a position in the filesystem",
    "\
@@ -1500,7 +1500,7 @@ on the volume group C<volgroup>, with C<size> megabytes.");
       [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount_options"; ""; "/dev/sda1"; "/"];
-       ["write_file"; "/new"; "new file contents"; "0"];
+       ["write"; "/new"; "new file contents"];
        ["cat"; "/new"]], "new file contents")],
    "make a filesystem",
    "\
@@ -1537,25 +1537,8 @@ the string C<,> (comma).
 See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>,
 C<guestfs_part_init>");
 
-  ("write_file", (RErr, [Pathname "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
-   [InitBasicFS, Always, TestOutput (
-      [["write_file"; "/new"; "new file contents"; "0"];
-       ["cat"; "/new"]], "new file contents");
-    InitBasicFS, Always, TestOutput (
-      [["write_file"; "/new"; "\nnew file contents\n"; "0"];
-       ["cat"; "/new"]], "\nnew file contents\n");
-    InitBasicFS, Always, TestOutput (
-      [["write_file"; "/new"; "\n\n"; "0"];
-       ["cat"; "/new"]], "\n\n");
-    InitBasicFS, Always, TestOutput (
-      [["write_file"; "/new"; ""; "0"];
-       ["cat"; "/new"]], "");
-    InitBasicFS, Always, TestOutput (
-      [["write_file"; "/new"; "\n\n\n"; "0"];
-       ["cat"; "/new"]], "\n\n\n");
-    InitBasicFS, Always, TestOutput (
-      [["write_file"; "/new"; "\n"; "0"];
-       ["cat"; "/new"]], "\n")],
+  ("write_file", (RErr, [Pathname "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning; DeprecatedBy "write"],
+   [],
    "create a file",
    "\
 This call creates a file called C<path>.  The contents of the
@@ -1567,9 +1550,7 @@ then the length is calculated using C<strlen> (so in this case
 the content cannot contain embedded ASCII NULs).
 
 I<NB.> Owing to a bug, writing content containing ASCII NUL
-characters does I<not> work, even if the length is specified.
-We hope to resolve this bug in a future version.  In the meantime
-use C<guestfs_upload>.");
+characters does I<not> work, even if the length is specified.");
 
   ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
    [InitEmpty, Always, TestOutputListOfDevices (
@@ -2088,7 +2069,7 @@ To download an uncompressed tarball, use C<guestfs_tar_out>.");
        ["mount_ro"; "/dev/sda1"; "/"];
        ["touch"; "/new"]]);
     InitBasicFS, Always, TestOutput (
-      [["write_file"; "/new"; "data"; "0"];
+      [["write"; "/new"; "data"];
        ["umount"; "/"];
        ["mount_ro"; "/dev/sda1"; "/"];
        ["cat"; "/new"]], "data")],
@@ -2340,15 +2321,15 @@ C<device>, with the root directory being C<root>.");
 
   ("cp", (RErr, [Pathname "src"; Pathname "dest"]), 87, [],
    [InitBasicFS, Always, TestOutput (
-      [["write_file"; "/old"; "file content"; "0"];
+      [["write"; "/old"; "file content"];
        ["cp"; "/old"; "/new"];
        ["cat"; "/new"]], "file content");
     InitBasicFS, Always, TestOutputTrue (
-      [["write_file"; "/old"; "file content"; "0"];
+      [["write"; "/old"; "file content"];
        ["cp"; "/old"; "/new"];
        ["is_file"; "/old"]]);
     InitBasicFS, Always, TestOutput (
-      [["write_file"; "/old"; "file content"; "0"];
+      [["write"; "/old"; "file content"];
        ["mkdir"; "/dir"];
        ["cp"; "/old"; "/dir/new"];
        ["cat"; "/dir/new"]], "file content")],
@@ -2361,7 +2342,7 @@ either a destination filename or destination directory.");
    [InitBasicFS, Always, TestOutput (
       [["mkdir"; "/olddir"];
        ["mkdir"; "/newdir"];
-       ["write_file"; "/olddir/file"; "file content"; "0"];
+       ["write"; "/olddir/file"; "file content"];
        ["cp_a"; "/olddir"; "/newdir"];
        ["cat"; "/newdir/olddir/file"]], "file content")],
    "copy a file or directory recursively",
@@ -2371,11 +2352,11 @@ recursively using the C<cp -a> command.");
 
   ("mv", (RErr, [Pathname "src"; Pathname "dest"]), 89, [],
    [InitBasicFS, Always, TestOutput (
-      [["write_file"; "/old"; "file content"; "0"];
+      [["write"; "/old"; "file content"];
        ["mv"; "/old"; "/new"];
        ["cat"; "/new"]], "file content");
     InitBasicFS, Always, TestOutputFalse (
-      [["write_file"; "/old"; "file content"; "0"];
+      [["write"; "/old"; "file content"];
        ["mv"; "/old"; "/new"];
        ["is_file"; "/old"]])],
    "move a file",
@@ -2424,12 +2405,12 @@ or attached block device(s) in any other way.");
 
   ("equal", (RBool "equality", [Pathname "file1"; Pathname "file2"]), 93, [],
    [InitBasicFS, Always, TestOutputTrue (
-      [["write_file"; "/file1"; "contents of a file"; "0"];
+      [["write"; "/file1"; "contents of a file"];
        ["cp"; "/file1"; "/file2"];
        ["equal"; "/file1"; "/file2"]]);
     InitBasicFS, Always, TestOutputFalse (
-      [["write_file"; "/file1"; "contents of a file"; "0"];
-       ["write_file"; "/file2"; "contents of another file"; "0"];
+      [["write"; "/file1"; "contents of a file"];
+       ["write"; "/file2"; "contents of another file"];
        ["equal"; "/file1"; "/file2"]]);
     InitBasicFS, Always, TestLastFail (
       [["equal"; "/file1"; "/file2"]])],
@@ -2456,8 +2437,8 @@ the list of printable strings found.");
   ("strings_e", (RStringList "stringsout", [String "encoding"; Pathname "path"]), 95, [ProtocolLimitWarning],
    [InitISOFS, Always, TestOutputList (
       [["strings_e"; "b"; "/known-5"]], []);
-    InitBasicFS, Disabled, TestOutputList (
-      [["write_file"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"; "24"];
+    InitBasicFS, Always, TestOutputList (
+      [["write"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"];
        ["strings_e"; "b"; "/new"]], ["hello"; "world"])],
    "print the printable strings in a file",
    "\
@@ -2521,7 +2502,7 @@ the human-readable, canonical hex dump of the file.");
       [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext3"; "/dev/sda1"];
        ["mount_options"; ""; "/dev/sda1"; "/"];
-       ["write_file"; "/new"; "test file"; "0"];
+       ["write"; "/new"; "test file"];
        ["umount"; "/dev/sda1"];
        ["zerofree"; "/dev/sda1"];
        ["mount_options"; ""; "/dev/sda1"; "/"];
@@ -2626,7 +2607,7 @@ are activated or deactivated.");
        ["lvcreate"; "LV"; "VG"; "10"];
        ["mkfs"; "ext2"; "/dev/VG/LV"];
        ["mount_options"; ""; "/dev/VG/LV"; "/"];
-       ["write_file"; "/new"; "test content"; "0"];
+       ["write"; "/new"; "test content"];
        ["umount"; "/"];
        ["lvresize"; "/dev/VG/LV"; "20"];
        ["e2fsck_f"; "/dev/VG/LV"];
@@ -2813,7 +2794,7 @@ manual page for more details.");
 
   ("scrub_file", (RErr, [Pathname "file"]), 115, [Optional "scrub"],
    [InitBasicFS, Always, TestRun (
-      [["write_file"; "/file"; "content"; "0"];
+      [["write"; "/file"; "content"];
        ["scrub_file"; "/file"]])],
    "scrub (securely wipe) a file",
    "\
@@ -3721,7 +3702,7 @@ and C<guestfs_setcon>");
       [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs_b"; "ext2"; "4096"; "/dev/sda1"];
        ["mount_options"; ""; "/dev/sda1"; "/"];
-       ["write_file"; "/new"; "new file contents"; "0"];
+       ["write"; "/new"; "new file contents"];
        ["cat"; "/new"]], "new file contents")],
    "make a filesystem with block size",
    "\
@@ -3736,7 +3717,7 @@ are C<1024>, C<2048> or C<4096> only.");
        ["mke2journal"; "4096"; "/dev/sda1"];
        ["mke2fs_J"; "ext2"; "4096"; "/dev/sda2"; "/dev/sda1"];
        ["mount_options"; ""; "/dev/sda2"; "/"];
-       ["write_file"; "/new"; "new file contents"; "0"];
+       ["write"; "/new"; "new file contents"];
        ["cat"; "/new"]], "new file contents")],
    "make ext2/3/4 external journal",
    "\
@@ -3751,7 +3732,7 @@ to the command:
        ["mke2journal_L"; "4096"; "JOURNAL"; "/dev/sda1"];
        ["mke2fs_JL"; "ext2"; "4096"; "/dev/sda2"; "JOURNAL"];
        ["mount_options"; ""; "/dev/sda2"; "/"];
-       ["write_file"; "/new"; "new file contents"; "0"];
+       ["write"; "/new"; "new file contents"];
        ["cat"; "/new"]], "new file contents")],
    "make ext2/3/4 external journal with label",
    "\
@@ -3764,7 +3745,7 @@ This creates an ext2 external journal on C<device> with label C<label>.");
         ["mke2journal_U"; "4096"; uuid; "/dev/sda1"];
         ["mke2fs_JU"; "ext2"; "4096"; "/dev/sda2"; uuid];
         ["mount_options"; ""; "/dev/sda2"; "/"];
-        ["write_file"; "/new"; "new file contents"; "0"];
+        ["write"; "/new"; "new file contents"];
         ["cat"; "/new"]], "new file contents")]),
    "make ext2/3/4 external journal with UUID",
    "\
@@ -3927,7 +3908,7 @@ if you used the C<guestfs_mount> call).");
 
   ("truncate", (RErr, [Pathname "path"]), 199, [],
    [InitBasicFS, Always, TestOutputStruct (
-      [["write_file"; "/test"; "some stuff so size is not zero"; "0"];
+      [["write"; "/test"; "some stuff so size is not zero"];
        ["truncate"; "/test"];
        ["stat"; "/test"]], [CompareWithInt ("size", 0)])],
    "truncate a file to zero size",
@@ -4081,7 +4062,9 @@ This command lets you read part of a file.  It reads C<count>
 bytes of the file, starting at C<offset>, from file C<path>.
 
 This may read fewer bytes than requested.  For further details
-see the L<pread(2)> system call.");
+see the L<pread(2)> system call.
+
+See also C<guestfs_pwrite>.");
 
   ("part_init", (RErr, [Device "device"; String "parttype"]), 208, [],
    [InitEmpty, Always, TestRun (
@@ -4345,7 +4328,7 @@ See also C<guestfs_version>.
 
   ("dd", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"]), 217, [],
    [InitBasicFS, Always, TestOutputBuffer (
-      [["write_file"; "/src"; "hello, world"; "0"];
+      [["write"; "/src"; "hello, world"];
        ["dd"; "/src"; "/dest"];
        ["read_file"; "/dest"]], "hello, world")],
    "copy from source to destination using dd",
@@ -4361,7 +4344,7 @@ This command cannot do partial copies (see C<guestfs_copy_size>).");
 
   ("filesize", (RInt64 "size", [Pathname "file"]), 218, [],
    [InitBasicFS, Always, TestOutputInt (
-      [["write_file"; "/file"; "hello, world"; "0"];
+      [["write"; "/file"; "hello, world"];
        ["filesize"; "/file"]], 12)],
    "return the size of the file in bytes",
    "\
@@ -4452,7 +4435,7 @@ See also C<guestfs_vgpvuuids>.");
 
   ("copy_size", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"; Int64 "size"]), 227, [],
    [InitBasicFS, Always, TestOutputBuffer (
-      [["write_file"; "/src"; "hello, world"; "0"];
+      [["write"; "/src"; "hello, world"];
        ["copy_size"; "/src"; "/dest"; "5"];
        ["read_file"; "/dest"]], "hello")],
    "copy size bytes from source to destination using dd",
@@ -4472,7 +4455,7 @@ This command writes zeroes over the entire C<device>.  Compare
 with C<guestfs_zero> which just zeroes the first few blocks of
 a device.");
 
-  ("txz_in", (RErr, [FileIn "tarball"; Pathname "directory"]), 229, [],
+  ("txz_in", (RErr, [FileIn "tarball"; Pathname "directory"]), 229, [Optional "xz"],
    [InitBasicFS, Always, TestOutput (
       [["txz_in"; "../images/helloworld.tar.xz"; "/"];
        ["cat"; "/hello"]], "hello\n")],
@@ -4481,7 +4464,7 @@ a device.");
 This command uploads and unpacks local file C<tarball> (an
 I<xz compressed> tar file) into C<directory>.");
 
-  ("txz_out", (RErr, [Pathname "directory"; FileOut "tarball"]), 230, [],
+  ("txz_out", (RErr, [Pathname "directory"; FileOut "tarball"]), 230, [Optional "xz"],
    [],
    "pack directory into compressed tarball",
    "\
@@ -4653,6 +4636,56 @@ a new file of length C<len> containing the repeating pattern
 of bytes in C<pattern>.  The pattern is truncated if necessary
 to ensure the length of the file is exactly C<len> bytes.");
 
+  ("write", (RErr, [Pathname "path"; BufferIn "content"]), 246, [ProtocolLimitWarning],
+   [InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "new file contents"];
+       ["cat"; "/new"]], "new file contents");
+    InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "\nnew file contents\n"];
+       ["cat"; "/new"]], "\nnew file contents\n");
+    InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "\n\n"];
+       ["cat"; "/new"]], "\n\n");
+    InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; ""];
+       ["cat"; "/new"]], "");
+    InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "\n\n\n"];
+       ["cat"; "/new"]], "\n\n\n");
+    InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "\n"];
+       ["cat"; "/new"]], "\n")],
+   "create a new file",
+   "\
+This call creates a file called C<path>.  The content of the
+file is the string C<content> (which can contain any 8 bit data).");
+
+  ("pwrite", (RInt "nbytes", [Pathname "path"; BufferIn "content"; Int64 "offset"]), 247, [ProtocolLimitWarning],
+   [InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "new file contents"];
+       ["pwrite"; "/new"; "data"; "4"];
+       ["cat"; "/new"]], "new data contents");
+    InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "new file contents"];
+       ["pwrite"; "/new"; "is extended"; "9"];
+       ["cat"; "/new"]], "new file is extended");
+    InitBasicFS, Always, TestOutput (
+      [["write"; "/new"; "new file contents"];
+       ["pwrite"; "/new"; ""; "4"];
+       ["cat"; "/new"]], "new file contents")],
+   "write to part of a file",
+   "\
+This command writes to part of a file.  It writes the data
+buffer C<content> to the file C<path> starting at offset C<offset>.
+
+This command implements the L<pwrite(2)> system call, and like
+that system call it may not write the full data requested.  The
+return value is the number of bytes that were actually written
+to the file.  This could even be 0, although short writes are
+unlikely for regular files in ordinary circumstances.
+
+See also C<guestfs_pread>.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -5802,6 +5835,50 @@ check_state (guestfs_h *g, const char *caller)
 
 ";
 
+  let error_code_of = function
+    | RErr | RInt _ | RInt64 _ | RBool _ -> "-1"
+    | RConstString _ | RConstOptString _
+    | RString _ | RStringList _
+    | RStruct _ | RStructList _
+    | RHashtable _ | RBufferOut _ -> "NULL"
+  in
+
+  (* Generate code to check String-like parameters are not passed in
+   * as NULL (returning an error if they are).
+   *)
+  let check_null_strings shortname style =
+    let pr_newline = ref false in
+    List.iter (
+      function
+      (* parameters which should not be NULL *)
+      | String n
+      | Device n
+      | Pathname n
+      | Dev_or_Path n
+      | FileIn n
+      | FileOut n
+      | BufferIn n
+      | StringList n
+      | DeviceList n ->
+          pr "  if (%s == NULL) {\n" n;
+          pr "    error (g, \"%%s: %%s: parameter cannot be NULL\",\n";
+          pr "           \"%s\", \"%s\");\n" shortname n;
+          pr "    return %s;\n" (error_code_of (fst style));
+          pr "  }\n";
+          pr_newline := true
+
+      (* can be NULL *)
+      | OptString _
+
+      (* not applicable *)
+      | Bool _
+      | Int _
+      | Int64 _ -> ()
+    ) (snd style);
+
+    if !pr_newline then pr "\n";
+  in
+
   (* Generate code to generate guestfish call traces. *)
   let trace_call shortname style =
     pr "  if (guestfs__get_trace (g)) {\n";
@@ -5859,6 +5936,7 @@ check_state (guestfs_h *g, const char *caller)
       generate_prototype ~extern:false ~semicolon:false ~newline:true
         ~handle:"g" name style;
       pr "{\n";
+      check_null_strings shortname style;
       trace_call shortname style;
       pr "  return guestfs__%s " shortname;
       generate_c_call_args ~handle:"g" style;
@@ -5871,21 +5949,12 @@ check_state (guestfs_h *g, const char *caller)
   List.iter (
     fun (shortname, style, _, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
+      let error_code = error_code_of (fst style) in
 
       (* Generate the action stub. *)
       generate_prototype ~extern:false ~semicolon:false ~newline:true
         ~handle:"g" name style;
 
-      let error_code =
-        match fst style with
-        | RErr | RInt _ | RInt64 _ | RBool _ -> "-1"
-        | RConstString _ | RConstOptString _ ->
-            failwithf "RConstString|RConstOptString cannot be used by daemon functions"
-        | RString _ | RStringList _
-        | RStruct _ | RStructList _
-        | RHashtable _ | RBufferOut _ ->
-            "NULL" in
-
       pr "{\n";
 
       (match snd style with
@@ -5910,6 +5979,7 @@ check_state (guestfs_h *g, const char *caller)
       pr "  int serial;\n";
       pr "  int r;\n";
       pr "\n";
+      check_null_strings shortname style;
       trace_call shortname style;
       pr "  if (check_state (g, \"%s\") == -1) return %s;\n"
         shortname error_code;