From: Richard Jones Date: Wed, 15 Apr 2009 09:44:27 +0000 (+0100) Subject: Added stat, lstat, statvfs and associated stat structures. X-Git-Tag: 0.9.2~8 X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;h=212a55d483c2a20e61f42211c0c64aab3645cb09;p=libguestfs.git Added stat, lstat, statvfs and associated stat structures. --- diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 1c52f7a..520ad1e 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -31,6 +31,7 @@ guestfsd_SOURCES = \ lvm.c \ mount.c \ proto.c \ + stat.c \ stubs.c \ sync.c \ ../src/guestfs_protocol.h \ diff --git a/daemon/stat.c b/daemon/stat.c new file mode 100644 index 0000000..743dc6e --- /dev/null +++ b/daemon/stat.c @@ -0,0 +1,155 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/guestfs_protocol.h" +#include "daemon.h" +#include "actions.h" + +guestfs_int_stat * +do_stat (const char *path) +{ + int r; + guestfs_int_stat *ret; + struct stat statbuf; + + NEED_ROOT (NULL); + ABS_PATH (path, NULL); + + CHROOT_IN; + r = stat (path, &statbuf); + CHROOT_OUT; + + if (r == -1) { + reply_with_perror ("stat"); + return NULL; + } + + ret = malloc (sizeof *ret); + if (ret == NULL) { + reply_with_perror ("malloc"); + return NULL; + } + + ret->dev = statbuf.st_dev; + ret->ino = statbuf.st_ino; + ret->mode = statbuf.st_mode; + ret->nlink = statbuf.st_nlink; + ret->uid = statbuf.st_uid; + ret->gid = statbuf.st_gid; + ret->rdev = statbuf.st_rdev; + ret->size = statbuf.st_size; + ret->blksize = statbuf.st_blksize; + ret->blocks = statbuf.st_blocks; + ret->atime = statbuf.st_atime; + ret->mtime = statbuf.st_mtime; + ret->ctime = statbuf.st_ctime; + + return ret; +} + +guestfs_int_stat * +do_lstat (const char *path) +{ + int r; + guestfs_int_stat *ret; + struct stat statbuf; + + NEED_ROOT (NULL); + ABS_PATH (path, NULL); + + CHROOT_IN; + r = lstat (path, &statbuf); + CHROOT_OUT; + + if (r == -1) { + reply_with_perror ("stat"); + return NULL; + } + + ret = malloc (sizeof *ret); + if (ret == NULL) { + reply_with_perror ("malloc"); + return NULL; + } + + ret->dev = statbuf.st_dev; + ret->ino = statbuf.st_ino; + ret->mode = statbuf.st_mode; + ret->nlink = statbuf.st_nlink; + ret->uid = statbuf.st_uid; + ret->gid = statbuf.st_gid; + ret->rdev = statbuf.st_rdev; + ret->size = statbuf.st_size; + ret->blksize = statbuf.st_blksize; + ret->blocks = statbuf.st_blocks; + ret->atime = statbuf.st_atime; + ret->mtime = statbuf.st_mtime; + ret->ctime = statbuf.st_ctime; + + return ret; +} + +guestfs_int_statvfs * +do_statvfs (const char *path) +{ + int r; + guestfs_int_statvfs *ret; + struct statvfs statbuf; + + NEED_ROOT (NULL); + ABS_PATH (path, NULL); + + CHROOT_IN; + r = statvfs (path, &statbuf); + CHROOT_OUT; + + if (r == -1) { + reply_with_perror ("statvfs"); + return NULL; + } + + ret = malloc (sizeof *ret); + if (ret == NULL) { + reply_with_perror ("malloc"); + return NULL; + } + + ret->bsize = statbuf.f_bsize; + ret->frsize = statbuf.f_frsize; + ret->blocks = statbuf.f_blocks; + ret->bfree = statbuf.f_bfree; + ret->bavail = statbuf.f_bavail; + ret->files = statbuf.f_files; + ret->ffree = statbuf.f_ffree; + ret->favail = statbuf.f_favail; + ret->fsid = statbuf.f_fsid; + ret->flag = statbuf.f_flag; + ret->namemax = statbuf.f_namemax; + + return ret; +} diff --git a/src/generator.ml b/src/generator.ml index 6ef8e1b..c32b0ed 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -65,6 +65,9 @@ and ret = | RPVList of string | RVGList of string | RLVList of string + (* Stat buffers. *) + | RStat of string + | RStatVFS of string and args = argt list (* Function parameters, guestfs handle is implicit. *) (* Note in future we should allow a "variable args" parameter as @@ -107,7 +110,7 @@ can easily destroy all your data>." * 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. + * Between each test we umount-all and lvm-remove-all (except InitNone). * * Don't assume anything about the previous contents of the block * devices. Use 'Init*' to create some initial scenarios. @@ -141,11 +144,21 @@ and test = * content). *) | TestOutputLength of seq * int + (* Run the command sequence and expect the output of the final + * command to be a structure. + *) + | TestOutputStruct of seq * test_field_compare list (* Run the command sequence and expect the final command (only) * to fail. *) | TestLastFail of seq +and test_field_compare = + | CompareWithInt of string * int + | CompareWithString of string * string + | CompareFieldsIntEq of string * string + | CompareFieldsStrEq of string * string + (* Some initial scenarios for testing. *) and test_init = (* Do nothing, block devices could contain random stuff including @@ -475,24 +488,21 @@ This returns a list of the logical volume device names See also C."); ("pvs_full", (RPVList "physvols", []), 12, [], - [InitBasicFSonLVM, TestOutputLength ( - [["pvs"]], 1)], + [], (* XXX how to test? *) "list the LVM physical volumes (PVs)", "\ List all the physical volumes detected. This is the equivalent of the L command. The \"full\" version includes all fields."); ("vgs_full", (RVGList "volgroups", []), 13, [], - [InitBasicFSonLVM, TestOutputLength ( - [["pvs"]], 1)], + [], (* XXX how to test? *) "list the LVM volume groups (VGs)", "\ List all the volumes groups detected. This is the equivalent of the L command. The \"full\" version includes all fields."); ("lvs_full", (RLVList "logvols", []), 14, [], - [InitBasicFSonLVM, TestOutputLength ( - [["pvs"]], 1)], + [], (* XXX how to test? *) "list the LVM logical volumes (LVs)", "\ List all the logical volumes detected. This is the equivalent @@ -1001,6 +1011,43 @@ locations."); This is the same as C, but splits the result into a list of lines."); + ("stat", (RStat "statbuf", [String "path"]), 52, [], + [InitBasicFS, TestOutputStruct ( + [["touch"; "/new"]; + ["stat"; "/new"]], [CompareWithInt ("size", 0)])], + "get file information", + "\ +Returns file information for the given C. + +This is the same as the C system call."); + + ("lstat", (RStat "statbuf", [String "path"]), 53, [], + [InitBasicFS, TestOutputStruct ( + [["touch"; "/new"]; + ["lstat"; "/new"]], [CompareWithInt ("size", 0)])], + "get file information for a symbolic link", + "\ +Returns file information for the given C. + +This is the same as C except that if C +is a symbolic link, then the link is stat-ed, not the file it +refers to. + +This is the same as the C system call."); + + ("statvfs", (RStatVFS "statbuf", [String "path"]), 54, [], + [InitBasicFS, TestOutputStruct ( + [["statvfs"; "/"]], [CompareWithInt ("bfree", 487702); + CompareWithInt ("blocks", 490020); + CompareWithInt ("bsize", 1024)])], + "get file system statistics", + "\ +Returns file system statistics for any mounted file system. +C should be a file or directory in the mounted file system +(typically it is the mount point itself, but it doesn't need to be). + +This is the same as the C system call."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -1075,6 +1122,39 @@ let lv_cols = [ "modules", `String; ] +(* Column names and types from stat structures. + * NB. Can't use things like 'st_atime' because glibc header files + * define some of these as macros. Ugh. + *) +let stat_cols = [ + "dev", `Int; + "ino", `Int; + "mode", `Int; + "nlink", `Int; + "uid", `Int; + "gid", `Int; + "rdev", `Int; + "size", `Int; + "blksize", `Int; + "blocks", `Int; + "atime", `Int; + "mtime", `Int; + "ctime", `Int; +] +let statvfs_cols = [ + "bsize", `Int; + "frsize", `Int; + "blocks", `Int; + "bfree", `Int; + "bavail", `Int; + "files", `Int; + "ffree", `Int; + "favail", `Int; + "fsid", `Int; + "flag", `Int; + "namemax", `Int; +] + (* Useful functions. * Note we don't want to use any external OCaml libraries which * makes this a bit harder than it should be. @@ -1203,7 +1283,8 @@ let check_functions () = (match fst style with | RErr -> () | RInt n | RBool n | RConstString n | RString n - | RStringList n | RPVList n | RVGList n | RLVList n -> + | RStringList n | RPVList n | RVGList n | RLVList n + | RStat n | RStatVFS n -> check_arg_ret_name n | RIntBool (n,m) -> check_arg_ret_name n; @@ -1345,17 +1426,34 @@ I.\n\n" (like L), or NULL if there was an error. I.\n\n" | RIntBool _ -> - pr "This function returns a C. + pr "This function returns a C, +or NULL if there was an error. I after use>.\n\n" | RPVList _ -> - pr "This function returns a C. + pr "This function returns a C +(see Eguestfs-structs.hE), +or NULL if there was an error. I after use>.\n\n" | RVGList _ -> - pr "This function returns a C. + pr "This function returns a C +(see Eguestfs-structs.hE), +or NULL if there was an error. I after use>.\n\n" | RLVList _ -> - pr "This function returns a C. + pr "This function returns a C +(see Eguestfs-structs.hE), +or NULL if there was an error. I after use>.\n\n" + | RStat _ -> + pr "This function returns a C +(see L and Eguestfs-structs.hE), +or NULL if there was an error. +I after use>.\n\n" + | RStatVFS _ -> + pr "This function returns a C +(see L and Eguestfs-structs.hE), +or NULL if there was an error. +I after use>.\n\n" ); if List.mem ProtocolLimitWarning flags then pr "%s\n\n" protocol_limit_warning; @@ -1426,6 +1524,18 @@ and generate_xdr () = pr "\n"; ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; + (* Stat internal structures. *) + List.iter ( + function + | typ, cols -> + pr "struct guestfs_int_%s {\n" typ; + List.iter (function + | name, `Int -> pr " hyper %s;\n" name + ) cols; + pr "};\n"; + pr "\n"; + ) ["stat", stat_cols; "statvfs", statvfs_cols]; + List.iter ( fun (shortname, style, _, _, _, _, _) -> let name = "guestfs_" ^ shortname in @@ -1481,6 +1591,14 @@ and generate_xdr () = pr "struct %s_ret {\n" name; pr " guestfs_lvm_int_lv_list %s;\n" n; pr "};\n\n" + | RStat n -> + pr "struct %s_ret {\n" name; + pr " guestfs_int_stat %s;\n" n; + pr "};\n\n"; + | RStatVFS n -> + pr "struct %s_ret {\n" name; + pr " guestfs_int_statvfs %s;\n" n; + pr "};\n\n"; ); ) daemon_functions; @@ -1579,7 +1697,20 @@ and generate_structs_h () = pr " struct guestfs_lvm_%s *val;\n" typ; pr "};\n"; pr "\n" - ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols] + ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; + + (* Stat structures. *) + List.iter ( + function + | typ, cols -> + pr "struct guestfs_%s {\n" typ; + List.iter ( + function + | name, `Int -> pr " int64_t %s;\n" name + ) cols; + pr "};\n"; + pr "\n" + ) ["stat", stat_cols; "statvfs", statvfs_cols] (* Generate the guestfs-actions.h file. *) and generate_actions_h () = @@ -1612,7 +1743,8 @@ and generate_client_actions () = | RInt _ | RBool _ | RString _ | RStringList _ | RIntBool _ - | RPVList _ | RVGList _ | RLVList _ -> + | RPVList _ | RVGList _ | RLVList _ + | RStat _ | RStatVFS _ -> pr " struct %s_ret ret;\n" name ); pr "};\n\n"; @@ -1641,7 +1773,8 @@ and generate_client_actions () = | RInt _ | RBool _ | RString _ | RStringList _ | RIntBool _ - | RPVList _ | RVGList _ | RLVList _ -> + | RPVList _ | RVGList _ | RLVList _ + | RStat _ | RStatVFS _ -> pr " if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name; pr " error (g, \"%s: failed to parse reply\");\n" name; pr " return;\n"; @@ -1663,7 +1796,8 @@ and generate_client_actions () = | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" | RString _ | RStringList _ | RIntBool _ - | RPVList _ | RVGList _ | RLVList _ -> + | RPVList _ | RVGList _ | RLVList _ + | RStat _ | RStatVFS _ -> "NULL" in pr "{\n"; @@ -1765,6 +1899,12 @@ and generate_client_actions () = | RLVList n -> pr " /* caller will free this */\n"; pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n + | RStat n -> + pr " /* caller will free this */\n"; + pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n + | RStatVFS n -> + pr " /* caller will free this */\n"; + pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n ); pr "}\n\n" @@ -1819,7 +1959,9 @@ and generate_daemon_actions () = | RIntBool _ -> pr " guestfs_%s_ret *r;\n" name; "NULL" | RPVList _ -> pr " guestfs_lvm_int_pv_list *r;\n"; "NULL" | RVGList _ -> pr " guestfs_lvm_int_vg_list *r;\n"; "NULL" - | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL" in + | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL" + | RStat _ -> pr " guestfs_int_stat *r;\n"; "NULL" + | RStatVFS _ -> pr " guestfs_int_statvfs *r;\n"; "NULL" in (match snd style with | [] -> () @@ -1894,17 +2036,7 @@ and generate_daemon_actions () = | RIntBool _ -> pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name; pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name - | RPVList n -> - pr " struct guestfs_%s_ret ret;\n" name; - pr " ret.%s = *r;\n" n; - pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name; - pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name - | RVGList n -> - pr " struct guestfs_%s_ret ret;\n" name; - pr " ret.%s = *r;\n" n; - pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name; - pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name - | RLVList n -> + | RPVList n | RVGList n | RLVList n | RStat n | RStatVFS n -> pr " struct guestfs_%s_ret ret;\n" name; pr " ret.%s = *r;\n" n; pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name; @@ -2156,6 +2288,7 @@ int main (int argc, char *argv[]) const char *srcdir; int fd; char buf[256]; + int nr_tests; g = guestfs_create (); if (g == NULL) { @@ -2262,11 +2395,12 @@ int main (int argc, char *argv[]) exit (1); } -" (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024); + nr_tests = %d; +" (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests; iteri ( fun i test_name -> - pr " printf (\"%3d/%3d %s\\n\");\n" (i+1) nr_tests test_name; + pr " printf (\"%3d/%%3d %s\\n\", nr_tests);\n" (i+1) test_name; pr " if (%s () == -1) {\n" test_name; pr " printf (\"%s FAILED\\n\");\n" test_name; pr " failed++;\n"; @@ -2284,8 +2418,7 @@ int main (int argc, char *argv[]) pr "\n"; pr " if (failed > 0) {\n"; - pr " printf (\"***** %%d / %d tests FAILED *****\\n\", failed);\n" - nr_tests; + pr " printf (\"***** %%d / %%d tests FAILED *****\\n\", failed, nr_tests);\n"; pr " exit (1);\n"; pr " }\n"; pr "\n"; @@ -2434,6 +2567,44 @@ and generate_one_test name i (init, test) = in List.iter (generate_test_command_call test_name) seq; generate_test_command_call ~test test_name last + | TestOutputStruct (seq, checks) -> + pr " /* TestOutputStruct for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + List.iter ( + function + | CompareWithInt (field, expected) -> + pr " if (r->%s != %d) {\n" field expected; + pr " fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n" + test_name field expected; + pr " (int) r->%s);\n" field; + pr " return -1;\n"; + pr " }\n" + | CompareWithString (field, expected) -> + pr " if (strcmp (r->%s, \"%s\") != 0) {\n" field expected; + pr " fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n" + test_name field expected; + pr " r->%s);\n" field; + pr " return -1;\n"; + pr " }\n" + | CompareFieldsIntEq (field1, field2) -> + pr " if (r->%s != r->%s) {\n" field1 field2; + pr " fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n" + test_name field1 field2; + pr " (int) r->%s, (int) r->%s);\n" field1 field2; + pr " return -1;\n"; + pr " }\n" + | CompareFieldsStrEq (field1, field2) -> + pr " if (strcmp (r->%s, r->%s) != 0) {\n" field1 field2; + pr " fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n" + test_name field1 field2; + pr " r->%s, r->%s);\n" field1 field2; + pr " return -1;\n"; + pr " }\n" + ) checks + 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 @@ -2494,17 +2665,17 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = pr " int i;\n"; "NULL" | RIntBool _ -> - pr " struct guestfs_int_bool *r;\n"; - "NULL" + pr " struct guestfs_int_bool *r;\n"; "NULL" | RPVList _ -> - pr " struct guestfs_lvm_pv_list *r;\n"; - "NULL" + pr " struct guestfs_lvm_pv_list *r;\n"; "NULL" | RVGList _ -> - pr " struct guestfs_lvm_vg_list *r;\n"; - "NULL" + pr " struct guestfs_lvm_vg_list *r;\n"; "NULL" | RLVList _ -> - pr " struct guestfs_lvm_lv_list *r;\n"; - "NULL" in + pr " struct guestfs_lvm_lv_list *r;\n"; "NULL" + | RStat _ -> + pr " struct guestfs_stat *r;\n"; "NULL" + | RStatVFS _ -> + pr " struct guestfs_statvfs *r;\n"; "NULL" in pr " suppress_error = %d;\n" (if expect_error then 1 else 0); pr " r = guestfs_%s (g" name; @@ -2555,6 +2726,8 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = pr " guestfs_free_lvm_vg_list (r);\n" | RLVList _ -> pr " guestfs_free_lvm_lv_list (r);\n" + | RStat _ | RStatVFS _ -> + pr " free (r);\n" ); pr " }\n" @@ -2694,6 +2867,21 @@ and generate_fish_cmds () = pr "\n"; ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; + (* print_{stat,statvfs} functions *) + List.iter ( + function + | typ, cols -> + pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ; + pr "{\n"; + List.iter ( + function + | name, `Int -> + pr " printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name + ) cols; + pr "}\n"; + pr "\n"; + ) ["stat", stat_cols; "statvfs", statvfs_cols]; + (* run_ actions *) List.iter ( fun (name, style, _, flags, _, _, _) -> @@ -2710,6 +2898,8 @@ and generate_fish_cmds () = | RPVList _ -> pr " struct guestfs_lvm_pv_list *r;\n" | RVGList _ -> pr " struct guestfs_lvm_vg_list *r;\n" | RLVList _ -> pr " struct guestfs_lvm_lv_list *r;\n" + | RStat _ -> pr " struct guestfs_stat *r;\n" + | RStatVFS _ -> pr " struct guestfs_statvfs *r;\n" ); List.iter ( function @@ -2797,6 +2987,16 @@ and generate_fish_cmds () = pr " print_lv_list (r);\n"; pr " guestfs_free_lvm_lv_list (r);\n"; pr " return 0;\n" + | RStat _ -> + pr " if (r == NULL) return -1;\n"; + pr " print_stat (r);\n"; + pr " free (r);\n"; + pr " return 0;\n" + | RStatVFS _ -> + pr " if (r == NULL) return -1;\n"; + pr " print_statvfs (r);\n"; + pr " free (r);\n"; + pr " return 0;\n" ); pr "}\n"; pr "\n" @@ -2976,6 +3176,12 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) | RLVList _ -> if not in_daemon then pr "struct guestfs_lvm_lv_list *" else pr "guestfs_lvm_int_lv_list *" + | RStat _ -> + if not in_daemon then pr "struct guestfs_stat *" + else pr "guestfs_int_stat *" + | RStatVFS _ -> + if not in_daemon then pr "struct guestfs_statvfs *" + else pr "guestfs_int_statvfs *" ); pr "%s%s (" prefix name; if handle = None && List.length (snd style) = 0 then @@ -3051,6 +3257,8 @@ val close : t -> unit "; generate_ocaml_lvm_structure_decls (); + generate_ocaml_stat_structure_decls (); + (* The actions. *) List.iter ( fun (name, style, _, _, _, shortdesc, _) -> @@ -3076,6 +3284,8 @@ let () = generate_ocaml_lvm_structure_decls (); + generate_ocaml_stat_structure_decls (); + (* The actions. *) List.iter ( fun (name, style, _, _, _, shortdesc, _) -> @@ -3166,6 +3376,30 @@ and generate_ocaml_c () = pr "\n"; ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; + (* Stat copy functions. *) + List.iter ( + fun (typ, cols) -> + pr "static CAMLprim value\n"; + pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ; + pr "{\n"; + pr " CAMLparam0 ();\n"; + pr " CAMLlocal2 (rv, v);\n"; + pr "\n"; + pr " rv = caml_alloc (%d, 0);\n" (List.length cols); + iteri ( + fun i col -> + (match col with + | name, `Int -> + pr " v = caml_copy_int64 (%s->%s);\n" typ name + ); + pr " Store_field (rv, %d, v);\n" i + ) cols; + pr " CAMLreturn (rv);\n"; + pr "}\n"; + pr "\n"; + ) ["stat", stat_cols; "statvfs", statvfs_cols]; + + (* The wrappers. *) List.iter ( fun (name, style, _, _, _, _, _) -> let params = @@ -3220,17 +3454,17 @@ and generate_ocaml_c () = pr " char **r;\n"; "NULL" | RIntBool _ -> - pr " struct guestfs_int_bool *r;\n"; - "NULL" + pr " struct guestfs_int_bool *r;\n"; "NULL" | RPVList _ -> - pr " struct guestfs_lvm_pv_list *r;\n"; - "NULL" + pr " struct guestfs_lvm_pv_list *r;\n"; "NULL" | RVGList _ -> - pr " struct guestfs_lvm_vg_list *r;\n"; - "NULL" + pr " struct guestfs_lvm_vg_list *r;\n"; "NULL" | RLVList _ -> - pr " struct guestfs_lvm_lv_list *r;\n"; - "NULL" in + pr " struct guestfs_lvm_lv_list *r;\n"; "NULL" + | RStat _ -> + pr " struct guestfs_stat *r;\n"; "NULL" + | RStatVFS _ -> + pr " struct guestfs_statvfs *r;\n"; "NULL" in pr "\n"; pr " caml_enter_blocking_section ();\n"; @@ -3276,6 +3510,12 @@ and generate_ocaml_c () = | RLVList _ -> pr " rv = copy_lvm_lv_list (r);\n"; pr " guestfs_free_lvm_lv_list (r);\n"; + | RStat _ -> + pr " rv = copy_stat (r);\n"; + pr " free (r);\n"; + | RStatVFS _ -> + pr " rv = copy_statvfs (r);\n"; + pr " free (r);\n"; ); pr " CAMLreturn (rv);\n"; @@ -3310,6 +3550,18 @@ and generate_ocaml_lvm_structure_decls () = pr "\n" ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols] +and generate_ocaml_stat_structure_decls () = + List.iter ( + fun (typ, cols) -> + pr "type %s = {\n" typ; + List.iter ( + function + | name, `Int -> pr " %s : int64;\n" name + ) cols; + pr "}\n"; + pr "\n" + ) ["stat", stat_cols; "statvfs", statvfs_cols] + and generate_ocaml_prototype ?(is_external = false) name style = if is_external then pr "external " else pr "val "; pr "%s : t -> " name; @@ -3332,6 +3584,8 @@ and generate_ocaml_prototype ?(is_external = false) name style = | RPVList _ -> pr "lvm_pv array" | RVGList _ -> pr "lvm_vg array" | RLVList _ -> pr "lvm_lv array" + | RStat _ -> pr "stat" + | RStatVFS _ -> pr "statvfs" ); if is_external then ( pr " = "; @@ -3442,7 +3696,8 @@ DESTROY (g) | RString _ -> pr "SV *\n" | RStringList _ | RIntBool _ - | RPVList _ | RVGList _ | RLVList _ -> + | RPVList _ | RVGList _ | RLVList _ + | RStat _ | RStatVFS _ -> pr "void\n" (* all lists returned implictly on the stack *) ); (* Call and arguments. *) @@ -3556,11 +3811,16 @@ DESTROY (g) pr " PUSHs (sv_2mortal (newSViv (r->b)));\n"; pr " guestfs_free_int_bool (r);\n"; | RPVList n -> - generate_perl_lvm_code "pv" pv_cols name style n do_cleanups; + generate_perl_lvm_code "pv" pv_cols name style n do_cleanups | RVGList n -> - generate_perl_lvm_code "vg" vg_cols name style n do_cleanups; + generate_perl_lvm_code "vg" vg_cols name style n do_cleanups | RLVList n -> - generate_perl_lvm_code "lv" lv_cols name style n do_cleanups; + generate_perl_lvm_code "lv" lv_cols name style n do_cleanups + | RStat n -> + generate_perl_stat_code "stat" stat_cols name style n do_cleanups + | RStatVFS n -> + generate_perl_stat_code + "statvfs" statvfs_cols name style n do_cleanups ); pr "\n" @@ -3603,6 +3863,24 @@ and generate_perl_lvm_code typ cols name style n do_cleanups = pr " }\n"; pr " guestfs_free_lvm_%s_list (%s);\n" typ n +and generate_perl_stat_code typ cols name style n do_cleanups = + pr "PREINIT:\n"; + pr " struct guestfs_%s *%s;\n" typ n; + pr " PPCODE:\n"; + pr " %s = guestfs_%s " n name; + generate_call_args ~handle:"g" style; + pr ";\n"; + do_cleanups (); + pr " if (%s == NULL)\n" n; + pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name; + pr " EXTEND (SP, %d);\n" (List.length cols); + List.iter ( + function + | name, `Int -> + pr " PUSHs (sv_2mortal (my_newSVll (%s->%s)));\n" n name + ) cols; + pr " free (%s);\n" n + (* Generate Sys/Guestfs.pm. *) and generate_perl_pm () = generate_header HashStyle LGPLv2; @@ -3734,6 +4012,8 @@ and generate_perl_prototype name style = | RPVList n | RVGList n | RLVList n -> pr "@%s = " n + | RStat n + | RStatVFS n -> pr "%%%s = " n ); pr "$h->%s (" name; let comma = ref false in @@ -3925,6 +4205,27 @@ py_guestfs_close (PyObject *self, PyObject *args) pr "\n" ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; + (* Stat structures, turned into Python dictionaries. *) + List.iter ( + fun (typ, cols) -> + pr "static PyObject *\n"; + pr "put_%s (struct guestfs_%s *%s)\n" typ typ typ; + pr "{\n"; + pr " PyObject *dict;\n"; + pr "\n"; + pr " dict = PyDict_New ();\n"; + List.iter ( + function + | name, `Int -> + pr " PyDict_SetItemString (dict, \"%s\",\n" name; + pr " PyLong_FromLongLong (%s->%s));\n" + typ name + ) cols; + pr " return dict;\n"; + pr "};\n"; + pr "\n"; + ) ["stat", stat_cols; "statvfs", statvfs_cols]; + (* Python wrapper functions. *) List.iter ( fun (name, style, _, _, _, _, _) -> @@ -3945,7 +4246,9 @@ py_guestfs_close (PyObject *self, PyObject *args) | RIntBool _ -> pr " struct guestfs_int_bool *r;\n"; "NULL" | RPVList n -> pr " struct guestfs_lvm_pv_list *r;\n"; "NULL" | RVGList n -> pr " struct guestfs_lvm_vg_list *r;\n"; "NULL" - | RLVList n -> pr " struct guestfs_lvm_lv_list *r;\n"; "NULL" in + | RLVList n -> pr " struct guestfs_lvm_lv_list *r;\n"; "NULL" + | RStat n -> pr " struct guestfs_stat *r;\n"; "NULL" + | RStatVFS n -> pr " struct guestfs_statvfs *r;\n"; "NULL" in List.iter ( function @@ -4039,6 +4342,12 @@ py_guestfs_close (PyObject *self, PyObject *args) | RLVList n -> pr " py_r = put_lvm_lv_list (r);\n"; pr " guestfs_free_lvm_lv_list (r);\n" + | RStat n -> + pr " py_r = put_stat (r);\n"; + pr " free (r);\n" + | RStatVFS n -> + pr " py_r = put_statvfs (r);\n"; + pr " free (r);\n" ); pr " return py_r;\n";