Add summary message, fix libruby detection.
[libguestfs.git] / src / generator.ml
index 6df04a5..bdd032c 100755 (executable)
@@ -380,6 +380,25 @@ This returns the current state as an opaque integer.  This is
 only useful for printing debug and internal error messages.
 
 For more information on states, see L<guestfs(3)>.");
+
+  ("set_busy", (RErr, []), -1, [NotInFish],
+   [],
+   "set state to busy",
+   "\
+This sets the state to C<BUSY>.  This is only used when implementing
+actions using the low-level API.
+
+For more information on states, see L<guestfs(3)>.");
+
+  ("set_ready", (RErr, []), -1, [NotInFish],
+   [],
+   "set state to ready",
+   "\
+This sets the state to C<READY>.  This is only used when implementing
+actions using the low-level API.
+
+For more information on states, see L<guestfs(3)>.");
+
 ]
 
 let daemon_functions = [
@@ -1253,9 +1272,11 @@ Reread the partition table on C<device>.
 
 This uses the L<blockdev(8)> command.");
 
-(*
   ("upload", (RErr, [FileIn "filename"; String "remotefilename"]), 66, [],
-   [],
+   [InitBasicFS, TestOutput (
+      (* Pick a file from cwd which isn't likely to change. *)
+    [["upload"; "COPYING.LIB"; "/COPYING.LIB"];
+     ["checksum"; "md5"; "/COPYING.LIB"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
    "upload a file from the local machine",
    "\
 Upload local file C<filename> to C<remotefilename> on the
@@ -1266,7 +1287,12 @@ C<filename> can also be a named pipe.
 See also C<guestfs_download>.");
 
   ("download", (RErr, [String "remotefilename"; FileOut "filename"]), 67, [],
-   [],
+   [InitBasicFS, TestOutput (
+      (* Pick a file from cwd which isn't likely to change. *)
+    [["upload"; "COPYING.LIB"; "/COPYING.LIB"];
+     ["download"; "/COPYING.LIB"; "testdownload.tmp"];
+     ["upload"; "testdownload.tmp"; "/upload"];
+     ["checksum"; "md5"; "/upload"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
    "download a file to the local machine",
    "\
 Download file C<remotefilename> and save it as C<filename>
@@ -1275,7 +1301,113 @@ on the local machine.
 C<filename> can also be a named pipe.
 
 See also C<guestfs_upload>, C<guestfs_cat>.");
-*)
+
+  ("checksum", (RString "checksum", [String "csumtype"; String "path"]), 68, [],
+   [InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "test\n"; "0"];
+       ["checksum"; "crc"; "/new"]], "935282863");
+    InitBasicFS, TestLastFail (
+      [["checksum"; "crc"; "/new"]]);
+    InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "test\n"; "0"];
+       ["checksum"; "md5"; "/new"]], "d8e8fca2dc0f896fd7cb4cb0031ba249");
+    InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "test\n"; "0"];
+       ["checksum"; "sha1"; "/new"]], "4e1243bd22c66e76c2ba9eddc1f91394e57f9f83");
+    InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "test\n"; "0"];
+       ["checksum"; "sha224"; "/new"]], "52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec");
+    InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "test\n"; "0"];
+       ["checksum"; "sha256"; "/new"]], "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2");
+    InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "test\n"; "0"];
+       ["checksum"; "sha384"; "/new"]], "109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d");
+    InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "test\n"; "0"];
+       ["checksum"; "sha512"; "/new"]], "0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123")],
+   "compute MD5, SHAx or CRC checksum of file",
+   "\
+This call computes the MD5, SHAx or CRC checksum of the
+file named C<path>.
+
+The type of checksum to compute is given by the C<csumtype>
+parameter which must have one of the following values:
+
+=over 4
+
+=item C<crc>
+
+Compute the cyclic redundancy check (CRC) specified by POSIX
+for the C<cksum> command.
+
+=item C<md5>
+
+Compute the MD5 hash (using the C<md5sum> program).
+
+=item C<sha1>
+
+Compute the SHA1 hash (using the C<sha1sum> program).
+
+=item C<sha224>
+
+Compute the SHA224 hash (using the C<sha224sum> program).
+
+=item C<sha256>
+
+Compute the SHA256 hash (using the C<sha256sum> program).
+
+=item C<sha384>
+
+Compute the SHA384 hash (using the C<sha384sum> program).
+
+=item C<sha512>
+
+Compute the SHA512 hash (using the C<sha512sum> program).
+
+=back
+
+The checksum is returned as a printable string.");
+
+  ("tar_in", (RErr, [FileIn "tarfile"; String "directory"]), 69, [],
+   [InitBasicFS, TestOutput (
+      [["tar_in"; "images/helloworld.tar"; "/"];
+       ["cat"; "/hello"]], "hello\n")],
+   "unpack tarfile to directory",
+   "\
+This command uploads and unpacks local file C<tarfile> (an
+I<uncompressed> tar file) into C<directory>.
+
+To upload a compressed tarball, use C<guestfs_tgz_in>.");
+
+  ("tar_out", (RErr, [String "directory"; FileOut "tarfile"]), 70, [],
+   [],
+   "pack directory into tarfile",
+   "\
+This command packs the contents of C<directory> and downloads
+it to local file C<tarfile>.
+
+To download a compressed tarball, use C<guestfs_tgz_out>.");
+
+  ("tgz_in", (RErr, [FileIn "tarball"; String "directory"]), 71, [],
+   [InitBasicFS, TestOutput (
+      [["tgz_in"; "images/helloworld.tar.gz"; "/"];
+       ["cat"; "/hello"]], "hello\n")],
+   "unpack compressed tarball to directory",
+   "\
+This command uploads and unpacks local file C<tarball> (a
+I<gzip compressed> tar file) into C<directory>.
+
+To upload an uncompressed tarball, use C<guestfs_tar_in>.");
+
+  ("tgz_out", (RErr, [String "directory"; FileOut "tarball"]), 72, [],
+   [],
+   "pack directory into compressed tarball",
+   "\
+This command packs the contents of C<directory> and downloads
+it to local file C<tarball>.
+
+To download an uncompressed tarball, use C<guestfs_tar_out>.");
 
 ]
 
@@ -1926,9 +2058,17 @@ and generate_xdr () =
 
   (* Message header, etc. *)
   pr "\
+/* The communication protocol is now documented in the guestfs(3)
+ * manpage.
+ */
+
 const GUESTFS_PROGRAM = 0x2000F5F5;
 const GUESTFS_PROTOCOL_VERSION = 1;
 
+/* These constants must be larger than any possible message length. */
+const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5;
+const GUESTFS_CANCEL_FLAG = 0xffffeeee;
+
 enum guestfs_message_direction {
   GUESTFS_DIRECTION_CALL = 0,        /* client -> daemon */
   GUESTFS_DIRECTION_REPLY = 1        /* daemon -> client */
@@ -1945,19 +2085,6 @@ struct guestfs_message_error {
   string error_message<GUESTFS_ERROR_LEN>;
 };
 
-/* For normal requests and replies (not involving any FileIn or
- * FileOut parameters), the protocol is:
- *
- * For requests:
- *   total length (header + args, but not including length word itself)
- *   header
- *   guestfs_foo_args struct
- * For replies:
- *   total length (as above)
- *   header
- *   guestfs_foo_ret struct
- */
-
 struct guestfs_message_header {
   unsigned prog;                     /* GUESTFS_PROGRAM */
   unsigned vers;                     /* GUESTFS_PROTOCOL_VERSION */
@@ -1967,23 +2094,6 @@ struct guestfs_message_header {
   guestfs_message_status status;
 };
 
-/* Chunked encoding used to transfer files, for FileIn and FileOut
- * parameters.
- *
- * For requests which have >= 1 FileIn parameter:
- *   length of header + args (but not length word itself, and not chunks)
- *   header
- *   guestfs_foo_args struct
- *   sequence of chunks for FileIn param #0
- *   sequence of chunks for FileIn param #1 etc
- *
- * For replies which have >= 1 FileOut parameter:
- *   length of header + ret (but not length word itself, and not chunks)
- *   header
- *   guestfs_foo_ret struct
- *   sequence of chunks for FileOut param #0
- *   sequence of chunks for FileOut param #1 etc
- */
 const GUESTFS_MAX_CHUNK_SIZE = 8192;
 
 struct guestfs_chunk {
@@ -2146,8 +2256,7 @@ check_state (guestfs_h *g, const char *caller)
       pr "  /* This flag is set by the callbacks, so we know we've done\n";
       pr "   * the callbacks as expected, and in the right sequence.\n";
       pr "   * 0 = not called, 1 = send called,\n";
-      pr "   * 2.. = send_file called,\n";
-      pr "   * 1000 = reply called.\n";
+      pr "   * 1001 = reply called.\n";
       pr "   */\n";
       pr "  int cb_sequence;\n";
       pr "  struct guestfs_message_header hdr;\n";
@@ -2167,24 +2276,14 @@ check_state (guestfs_h *g, const char *caller)
       pr "};\n";
       pr "\n";
 
-      (* Generate the send callback function. *)
-      pr "static void %s_send_cb (guestfs_h *g, void *data)\n" shortname;
-      pr "{\n";
-      pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
-      pr "  struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
-      pr "\n";
-      pr "  guestfs__switch_to_receiving (g);\n";
-      pr "  ctx->cb_sequence = 1;\n";
-      pr "  ml->main_loop_quit (ml, g);\n";
-      pr "}\n";
-      pr "\n";
-
       (* Generate the reply callback function. *)
       pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
       pr "{\n";
       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
       pr "  struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
       pr "\n";
+      pr "  ml->main_loop_quit (ml, g);\n";
+      pr "\n";
       pr "  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {\n";
       pr "    error (g, \"%%s: failed to parse reply header\", \"%s\");\n" name;
       pr "    return;\n";
@@ -2215,8 +2314,7 @@ check_state (guestfs_h *g, const char *caller)
       );
 
       pr " done:\n";
-      pr "  ctx->cb_sequence = 1000;\n";
-      pr "  ml->main_loop_quit (ml, g);\n";
+      pr "  ctx->cb_sequence = 1001;\n";
       pr "}\n\n";
 
       (* Generate the action stub. *)
@@ -2246,14 +2344,15 @@ check_state (guestfs_h *g, const char *caller)
       pr "  int serial;\n";
       pr "\n";
       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
+      pr "  guestfs_set_busy (g);\n";
       pr "\n";
       pr "  memset (&ctx, 0, sizeof ctx);\n";
       pr "\n";
 
-      (* Dispatch the main header and arguments. *)
+      (* Send the main header and arguments. *)
       (match snd style with
        | [] ->
-          pr "  serial = guestfs__send (g, GUESTFS_PROC_%s, NULL, NULL);\n"
+          pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s, NULL, NULL);\n"
             (String.uppercase shortname)
        | args ->
           List.iter (
@@ -2271,54 +2370,62 @@ check_state (guestfs_h *g, const char *caller)
                 pr "  args.%s = %s;\n" n n
             | FileIn _ | FileOut _ -> ()
           ) args;
-          pr "  serial = guestfs__send (g, GUESTFS_PROC_%s,\n"
+          pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s,\n"
             (String.uppercase shortname);
           pr "        (xdrproc_t) xdr_%s_args, (char *) &args);\n"
             name;
       );
-      pr "  if (serial == -1)\n";
-      pr "    return %s;\n" error_code;
-      pr "\n";
-
-      (* Send the request. *)
-      pr "  ctx.cb_sequence = 0;\n";
-      pr "  guestfs_set_send_callback (g, %s_send_cb, &ctx);\n" shortname;
-      pr "  (void) ml->main_loop_run (ml, g);\n";
-      pr "  guestfs_set_send_callback (g, NULL, NULL);\n";
-      pr "  if (ctx.cb_sequence != 1) {\n";
-      pr "    error (g, \"%%s send failed, see earlier error messages\", \"%s\");\n" name;
+      pr "  if (serial == -1) {\n";
+      pr "    guestfs_set_ready (g);\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
 
-      (* Send any additional files requested. *)
+      (* Send any additional files (FileIn) requested. *)
+      let need_read_reply_label = ref false in
       List.iter (
        function
        | FileIn n ->
-           pr "  if (send_file (g, %s) == -1)\n" n;
-           pr "    return %s;\n" error_code;
+           pr "  {\n";
+           pr "    int r;\n";
+           pr "\n";
+           pr "    r = guestfs__send_file_sync (g, %s);\n" n;
+           pr "    if (r == -1) {\n";
+           pr "      guestfs_set_ready (g);\n";
+           pr "      return %s;\n" error_code;
+           pr "    }\n";
+           pr "    if (r == -2) /* daemon cancelled */\n";
+           pr "      goto read_reply;\n";
+           need_read_reply_label := true;
+           pr "  }\n";
            pr "\n";
        | _ -> ()
       ) (snd style);
 
       (* Wait for the reply from the remote end. *)
+      if !need_read_reply_label then pr " read_reply:\n";
+      pr "  guestfs__switch_to_receiving (g);\n";
       pr "  ctx.cb_sequence = 0;\n";
       pr "  guestfs_set_reply_callback (g, %s_reply_cb, &ctx);\n" shortname;
       pr "  (void) ml->main_loop_run (ml, g);\n";
       pr "  guestfs_set_reply_callback (g, NULL, NULL);\n";
-      pr "  if (ctx.cb_sequence != 1000) {\n";
+      pr "  if (ctx.cb_sequence != 1001) {\n";
       pr "    error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name;
+      pr "    guestfs_set_ready (g);\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
 
-      pr "  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1)\n"
+      pr "  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1) {\n"
        (String.uppercase shortname);
+      pr "    guestfs_set_ready (g);\n";
       pr "    return %s;\n" error_code;
+      pr "  }\n";
       pr "\n";
 
       pr "  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {\n";
       pr "    error (g, \"%%s\", ctx.err.error_message);\n";
+      pr "    guestfs_set_ready (g);\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
@@ -2327,12 +2434,16 @@ check_state (guestfs_h *g, const char *caller)
       List.iter (
        function
        | FileOut n ->
-           pr "  if (receive_file (g, %s) == -1)\n" n;
+           pr "  if (guestfs__receive_file_sync (g, %s) == -1) {\n" n;
+           pr "    guestfs_set_ready (g);\n";
            pr "    return %s;\n" error_code;
+           pr "  }\n";
            pr "\n";
        | _ -> ()
       ) (snd style);
 
+      pr "  guestfs_set_ready (g);\n";
+
       (match fst style with
        | RErr -> pr "  return 0;\n"
        | RInt n | RInt64 n | RBool n ->
@@ -2785,8 +2896,8 @@ int main (int argc, char *argv[])
   char c = 0;
   int failed = 0;
   const char *srcdir;
+  const char *filename;
   int fd;
-  char buf[256];
   int nr_tests, test_num = 0;
 
   no_test_warnings ();
@@ -2801,89 +2912,90 @@ int main (int argc, char *argv[])
 
   srcdir = getenv (\"srcdir\");
   if (!srcdir) srcdir = \".\";
-  guestfs_set_path (g, srcdir);
+  chdir (srcdir);
+  guestfs_set_path (g, \".\");
 
-  snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir);
-  fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
+  filename = \"test1.img\";
+  fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
   if (fd == -1) {
-    perror (buf);
+    perror (filename);
     exit (1);
   }
   if (lseek (fd, %d, SEEK_SET) == -1) {
     perror (\"lseek\");
     close (fd);
-    unlink (buf);
+    unlink (filename);
     exit (1);
   }
   if (write (fd, &c, 1) == -1) {
     perror (\"write\");
     close (fd);
-    unlink (buf);
+    unlink (filename);
     exit (1);
   }
   if (close (fd) == -1) {
-    perror (buf);
-    unlink (buf);
+    perror (filename);
+    unlink (filename);
     exit (1);
   }
-  if (guestfs_add_drive (g, buf) == -1) {
-    printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
+  if (guestfs_add_drive (g, filename) == -1) {
+    printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
     exit (1);
   }
 
-  snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir);
-  fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
+  filename = \"test2.img\";
+  fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
   if (fd == -1) {
-    perror (buf);
+    perror (filename);
     exit (1);
   }
   if (lseek (fd, %d, SEEK_SET) == -1) {
     perror (\"lseek\");
     close (fd);
-    unlink (buf);
+    unlink (filename);
     exit (1);
   }
   if (write (fd, &c, 1) == -1) {
     perror (\"write\");
     close (fd);
-    unlink (buf);
+    unlink (filename);
     exit (1);
   }
   if (close (fd) == -1) {
-    perror (buf);
-    unlink (buf);
+    perror (filename);
+    unlink (filename);
     exit (1);
   }
-  if (guestfs_add_drive (g, buf) == -1) {
-    printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
+  if (guestfs_add_drive (g, filename) == -1) {
+    printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
     exit (1);
   }
 
-  snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir);
-  fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
+  filename = \"test3.img\";
+  fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
   if (fd == -1) {
-    perror (buf);
+    perror (filename);
     exit (1);
   }
   if (lseek (fd, %d, SEEK_SET) == -1) {
     perror (\"lseek\");
     close (fd);
-    unlink (buf);
+    unlink (filename);
     exit (1);
   }
   if (write (fd, &c, 1) == -1) {
     perror (\"write\");
     close (fd);
-    unlink (buf);
+    unlink (filename);
     exit (1);
   }
   if (close (fd) == -1) {
-    perror (buf);
-    unlink (buf);
+    perror (filename);
+    unlink (filename);
     exit (1);
   }
-  if (guestfs_add_drive (g, buf) == -1) {
-    printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
+  if (guestfs_add_drive (g, filename) == -1) {
+    printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
     exit (1);
   }
 
@@ -2912,12 +3024,9 @@ int main (int argc, char *argv[])
   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 "  unlink (\"test1.img\");\n";
+  pr "  unlink (\"test2.img\");\n";
+  pr "  unlink (\"test3.img\");\n";
   pr "\n";
 
   pr "  if (failed > 0) {\n";