For more information on states, see L<guestfs(3)>.");
+ ("end_busy", (RErr, []), -1, [NotInFish],
+ [],
+ "leave the busy state",
+ "\
+This sets the state to C<READY>, or if in C<CONFIG> then it leaves the
+state as is. This is only used when implementing
+actions using the low-level API.
+
+For more information on states, see L<guestfs(3)>.");
+
]
let daemon_functions = [
As a special case, if C<size> is C<0>
then the length is calculated using C<strlen> (so in this case
-the content cannot contain embedded ASCII NULs).");
+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>.");
("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
[InitEmpty, TestOutputList (
The external L<cmp(1)> program is used for the comparison.");
+ ("strings", (RStringList "stringsout", [String "path"]), 94, [ProtocolLimitWarning],
+ [InitBasicFS, TestOutputList (
+ [["write_file"; "/new"; "hello\nworld\n"; "0"];
+ ["strings"; "/new"]], ["hello"; "world"])],
+ "print the printable strings in a file",
+ "\
+This runs the L<strings(1)> command on a file and returns
+the list of printable strings found.");
+
+ ("strings_e", (RStringList "stringsout", [String "encoding"; String "path"]), 95, [ProtocolLimitWarning],
+ [InitBasicFS, TestOutputList (
+ [["write_file"; "/new"; "hello\nworld\n"; "0"];
+ ["strings_e"; "b"; "/new"]], []);
+ (*InitBasicFS, TestOutputList (
+ [["write_file"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"; "24"];
+ ["strings_e"; "b"; "/new"]], ["hello"; "world"])*)],
+ "print the printable strings in a file",
+ "\
+This is like the C<guestfs_strings> command, but allows you to
+specify the encoding.
+
+See the L<strings(1)> manpage for the full list of encodings.
+
+Commonly useful encodings are C<l> (lower case L) which will
+show strings inside Windows/x86 files.
+
+The returned strings are transcoded to UTF-8.");
+
+ ("hexdump", (RString "dump", [String "path"]), 96, [ProtocolLimitWarning],
+ [InitBasicFS, TestOutput (
+ [["write_file"; "/new"; "hello\nworld\n"; "12"];
+ ["hexdump"; "/new"]], "00000000 68 65 6c 6c 6f 0a 77 6f 72 6c 64 0a |hello.world.|\n0000000c\n")],
+ "dump a file in hexadecimal",
+ "\
+This runs C<hexdump -C> on the given C<path>. The result is
+the human-readable, canonical hex dump of the file.");
+
]
let all_functions = non_daemon_functions @ daemon_functions
let pr fs = ksprintf (output_string !chan) fs
(* Generate a header block in a number of standard styles. *)
-type comment_style = CStyle | HashStyle | OCamlStyle
+type comment_style = CStyle | HashStyle | OCamlStyle | HaskellStyle
type license = GPLv2 | LGPLv2
let generate_header comment license =
let c = match comment with
| CStyle -> pr "/* "; " *"
| HashStyle -> pr "# "; "#"
- | OCamlStyle -> pr "(* "; " *" in
+ | OCamlStyle -> pr "(* "; " *"
+ | HaskellStyle -> pr "{- "; " " in
pr "libguestfs generated file\n";
pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
| CStyle -> pr " */\n"
| HashStyle -> ()
| OCamlStyle -> pr " *)\n"
+ | HaskellStyle -> pr "-}\n"
);
pr "\n"
name;
);
pr " if (serial == -1) {\n";
- pr " guestfs_set_ready (g);\n";
+ pr " guestfs_end_busy (g);\n";
pr " return %s;\n" error_code;
pr " }\n";
pr "\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 " guestfs_end_busy (g);\n";
pr " return %s;\n" error_code;
pr " }\n";
pr " if (r == -2) /* daemon cancelled */\n";
pr " guestfs_set_reply_callback (g, NULL, NULL);\n";
pr " if (ctx.cb_sequence != 1) {\n";
pr " error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name;
- pr " guestfs_set_ready (g);\n";
+ pr " guestfs_end_busy (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"
(String.uppercase shortname);
- pr " guestfs_set_ready (g);\n";
+ pr " guestfs_end_busy (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 " free (ctx.err.error_message);\n";
+ pr " guestfs_end_busy (g);\n";
pr " return %s;\n" error_code;
pr " }\n";
pr "\n";
function
| FileOut n ->
pr " if (guestfs__receive_file_sync (g, %s) == -1) {\n" n;
- pr " guestfs_set_ready (g);\n";
+ pr " guestfs_end_busy (g);\n";
pr " return %s;\n" error_code;
pr " }\n";
pr "\n";
| _ -> ()
) (snd style);
- pr " guestfs_set_ready (g);\n";
+ pr " guestfs_end_busy (g);\n";
(match fst style with
| RErr -> pr " return 0;\n"
let str = replace_str str "\r" "\\r" in
let str = replace_str str "\n" "\\n" in
let str = replace_str str "\t" "\\t" in
+ let str = replace_str str "\000" "\\0" in
str
(* Generate a lot of different functions for guestfish. *)
croak (\"array reference expected\");
av = (AV *)SvRV (arg);
- ret = malloc (av_len (av) + 1 + 1);
+ ret = malloc ((av_len (av) + 1 + 1) * sizeof (char *));
if (!ret)
croak (\"malloc failed\");
pr " guestfs_free_lvm_%s_list (r);\n" typ;
pr " return jr;\n"
+and generate_haskell_hs () =
+ generate_header HaskellStyle LGPLv2;
+
+ (* XXX We only know how to generate partial FFI for Haskell
+ * at the moment. Please help out!
+ *)
+ let can_generate style =
+ let check_no_bad_args =
+ List.for_all (function Bool _ | Int _ -> false | _ -> true)
+ in
+ match style with
+ | RErr, args -> check_no_bad_args args
+ | RBool _, _
+ | RInt _, _
+ | RInt64 _, _
+ | RConstString _, _
+ | RString _, _
+ | RStringList _, _
+ | RIntBool _, _
+ | RPVList _, _
+ | RVGList _, _
+ | RLVList _, _
+ | RStat _, _
+ | RStatVFS _, _
+ | RHashtable _, _ -> false in
+
+ pr "\
+{-# INCLUDE <guestfs.h> #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+module Guestfs (
+ create";
+
+ (* List out the names of the actions we want to export. *)
+ List.iter (
+ fun (name, style, _, _, _, _, _) ->
+ if can_generate style then pr ",\n %s" name
+ ) all_functions;
+
+ pr "
+ ) where
+import Foreign
+import Foreign.C
+import IO
+import Control.Exception
+import Data.Typeable
+
+data GuestfsS = GuestfsS -- represents the opaque C struct
+type GuestfsP = Ptr GuestfsS -- guestfs_h *
+type GuestfsH = ForeignPtr GuestfsS -- guestfs_h * with attached finalizer
+
+-- XXX define properly later XXX
+data PV = PV
+data VG = VG
+data LV = LV
+data IntBool = IntBool
+data Stat = Stat
+data StatVFS = StatVFS
+data Hashtable = Hashtable
+
+foreign import ccall unsafe \"guestfs_create\" c_create
+ :: IO GuestfsP
+foreign import ccall unsafe \"&guestfs_close\" c_close
+ :: FunPtr (GuestfsP -> IO ())
+foreign import ccall unsafe \"guestfs_set_error_handler\" c_set_error_handler
+ :: GuestfsP -> Ptr CInt -> Ptr CInt -> IO ()
+
+create :: IO GuestfsH
+create = do
+ p <- c_create
+ c_set_error_handler p nullPtr nullPtr
+ h <- newForeignPtr c_close p
+ return h
+
+foreign import ccall unsafe \"guestfs_last_error\" c_last_error
+ :: GuestfsP -> IO CString
+
+-- last_error :: GuestfsH -> IO (Maybe String)
+-- last_error h = do
+-- str <- withForeignPtr h (\\p -> c_last_error p)
+-- maybePeek peekCString str
+
+last_error :: GuestfsH -> IO (String)
+last_error h = do
+ str <- withForeignPtr h (\\p -> c_last_error p)
+ if (str == nullPtr)
+ then return \"no error\"
+ else peekCString str
+
+";
+
+ (* Generate wrappers for each foreign function. *)
+ List.iter (
+ fun (name, style, _, _, _, _, _) ->
+ if can_generate style then (
+ pr "foreign import ccall unsafe \"guestfs_%s\" c_%s\n" name name;
+ pr " :: ";
+ generate_haskell_prototype ~handle:"GuestfsP" style;
+ pr "\n";
+ pr "\n";
+ pr "%s :: " name;
+ generate_haskell_prototype ~handle:"GuestfsH" ~hs:true style;
+ pr "\n";
+ pr "%s %s = do\n" name
+ (String.concat " " ("h" :: List.map name_of_argt (snd style)));
+ pr " r <- ";
+ List.iter (
+ function
+ | FileIn n
+ | FileOut n
+ | String n -> pr "withCString %s $ \\%s -> " n n
+ | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n
+ | StringList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n
+ | Bool n ->
+ (* XXX this doesn't work *)
+ pr " let\n";
+ pr " %s = case %s of\n" n n;
+ pr " False -> 0\n";
+ pr " True -> 1\n";
+ pr " in fromIntegral %s $ \\%s ->\n" n n
+ | Int n -> pr "fromIntegral %s $ \\%s -> " n n
+ ) (snd style);
+ pr "withForeignPtr h (\\p -> c_%s %s)\n" name
+ (String.concat " " ("p" :: List.map name_of_argt (snd style)));
+ (match fst style with
+ | RErr | RInt _ | RInt64 _ | RBool _ ->
+ pr " if (r == -1)\n";
+ pr " then do\n";
+ pr " err <- last_error h\n";
+ pr " fail err\n";
+ | RConstString _ | RString _ | RStringList _ | RIntBool _
+ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _
+ | RHashtable _ ->
+ pr " if (r == nullPtr)\n";
+ pr " then do\n";
+ pr " err <- last_error h\n";
+ pr " fail err\n";
+ );
+ (match fst style with
+ | RErr ->
+ pr " else return ()\n"
+ | RInt _ ->
+ pr " else return (fromIntegral r)\n"
+ | RInt64 _ ->
+ pr " else return (fromIntegral r)\n"
+ | RBool _ ->
+ pr " else return (toBool r)\n"
+ | RConstString _
+ | RString _
+ | RStringList _
+ | RIntBool _
+ | RPVList _
+ | RVGList _
+ | RLVList _
+ | RStat _
+ | RStatVFS _
+ | RHashtable _ ->
+ pr " else return ()\n" (* XXXXXXXXXXXXXXXXXXXX *)
+ );
+ pr "\n";
+ )
+ ) all_functions
+
+and generate_haskell_prototype ~handle ?(hs = false) style =
+ pr "%s -> " handle;
+ let string = if hs then "String" else "CString" in
+ let int = if hs then "Int" else "CInt" in
+ let bool = if hs then "Bool" else "CInt" in
+ let int64 = if hs then "Integer" else "Int64" in
+ List.iter (
+ fun arg ->
+ (match arg with
+ | String _ -> pr "%s" string
+ | OptString _ -> if hs then pr "Maybe String" else pr "CString"
+ | StringList _ -> if hs then pr "[String]" else pr "Ptr CString"
+ | Bool _ -> pr "%s" bool
+ | Int _ -> pr "%s" int
+ | FileIn _ -> pr "%s" string
+ | FileOut _ -> pr "%s" string
+ );
+ pr " -> ";
+ ) (snd style);
+ pr "IO (";
+ (match fst style with
+ | RErr -> if not hs then pr "CInt"
+ | RInt _ -> pr "%s" int
+ | RInt64 _ -> pr "%s" int64
+ | RBool _ -> pr "%s" bool
+ | RConstString _ -> pr "%s" string
+ | RString _ -> pr "%s" string
+ | RStringList _ -> pr "[%s]" string
+ | RIntBool _ -> pr "IntBool"
+ | RPVList _ -> pr "[PV]"
+ | RVGList _ -> pr "[VG]"
+ | RLVList _ -> pr "[LV]"
+ | RStat _ -> pr "Stat"
+ | RStatVFS _ -> pr "StatVFS"
+ | RHashtable _ -> pr "Hashtable"
+ );
+ pr ")"
+
let output_to filename =
let filename_new = filename ^ ".new" in
chan := open_out filename_new;
let close = output_to "java/com_redhat_et_libguestfs_GuestFS.c" in
generate_java_c ();
close ();
+
+ let close = output_to "haskell/Guestfs.hs" in
+ generate_haskell_hs ();
+ close ();