From d5ae4a54e83018687ec05255bc39e7f5ab71a453 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Wed, 15 Apr 2009 13:58:44 +0100 Subject: [PATCH] Added tune2fs-l command and RHashtable return type. --- daemon/Makefile.am | 1 + daemon/tune2fs.c | 119 ++++++++++++++++++++++++++++++++ src/generator.ml | 196 ++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 268 insertions(+), 48 deletions(-) create mode 100644 daemon/tune2fs.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 520ad1e..6da149b 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -34,6 +34,7 @@ guestfsd_SOURCES = \ stat.c \ stubs.c \ sync.c \ + tune2fs.c \ ../src/guestfs_protocol.h \ ../src/guestfs_protocol.c diff --git a/daemon/tune2fs.c b/daemon/tune2fs.c new file mode 100644 index 0000000..adf2a98 --- /dev/null +++ b/daemon/tune2fs.c @@ -0,0 +1,119 @@ +/* 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 + +#define _GNU_SOURCE // for strchrnul + +#include +#include +#include +#include + +#include "../src/guestfs_protocol.h" +#include "daemon.h" +#include "actions.h" + +char ** +do_tune2fs_l (const char *device) +{ + int r; + char *out, *err; + char *p, *pend, *colon; + char **ret = NULL; + int size = 0, alloc = 0; + + IS_DEVICE (device, NULL); + + r = command (&out, &err, "/sbin/tune2fs", "-l", device, NULL); + if (r == -1) { + reply_with_error ("tune2fs: %s", err); + free (err); + free (out); + return NULL; + } + free (err); + + p = out; + + /* Discard the first line if it contains "tune2fs ...". */ + if (strncmp (p, "tune2fs ", 8) == 0) { + p = strchr (p, '\n'); + if (p) p++; + else { + reply_with_error ("tune2fs: truncated output"); + free (out); + return NULL; + } + } + + /* Read the lines and split into "key: value". */ + while (*p) { + pend = strchrnul (p, '\n'); + if (*pend == '\n') { + *pend = '\0'; + pend++; + } + + if (!*p) continue; + + colon = strchr (p, ':'); + if (colon) { + *colon = '\0'; + + do { colon++; } while (*colon && isspace (*colon)); + + if (add_string (&ret, &size, &alloc, p) == -1) { + free (out); + return NULL; + } + if (strcmp (colon, "") == 0 || + strcmp (colon, "") == 0 || + strcmp (colon, "(none)") == 0) { + if (add_string (&ret, &size, &alloc, "") == -1) { + free (out); + return NULL; + } + } else { + if (add_string (&ret, &size, &alloc, colon) == -1) { + free (out); + return NULL; + } + } + } + else { + if (add_string (&ret, &size, &alloc, p) == -1) { + free (out); + return NULL; + } + if (add_string (&ret, &size, &alloc, "") == -1) { + free (out); + return NULL; + } + } + + p = pend; + } + + free (out); + + if (add_string (&ret, &size, &alloc, NULL) == -1) + return NULL; + + return ret; +} diff --git a/src/generator.ml b/src/generator.ml index c32b0ed..b1071a8 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -68,6 +68,15 @@ and ret = (* Stat buffers. *) | RStat of string | RStatVFS of string + (* Key-value pairs of untyped strings. Turns into a hashtable or + * dictionary in languages which support it. DON'T use this as a + * general "bucket" for results. Prefer a stronger typed return + * value if one is available, or write a custom struct. Don't use + * this if the list could potentially be very long, since it is + * inefficient. Keys should be unique. NULLs are not permitted. + *) + | RHashtable of string + and args = argt list (* Function parameters, guestfs handle is implicit. *) (* Note in future we should allow a "variable args" parameter as @@ -1048,6 +1057,18 @@ C should be a file or directory in the mounted file system This is the same as the C system call."); + ("tune2fs_l", (RHashtable "superblock", [String "device"]), 55, [], + [], (* XXX test *) + "get ext2/ext3 superblock details", + "\ +This returns the contents of the ext2 or ext3 filesystem superblock +on C. + +It is the same as running C. See L +manpage for more details. The list of fields returned isn't +clearly defined, and depends on both the version of C +that libguestfs was built against, and the filesystem itself."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -1284,7 +1305,8 @@ let check_functions () = | RErr -> () | RInt n | RBool n | RConstString n | RString n | RStringList n | RPVList n | RVGList n | RLVList n - | RStat n | RStatVFS n -> + | RStat n | RStatVFS n + | RHashtable n -> check_arg_ret_name n | RIntBool (n,m) -> check_arg_ret_name n; @@ -1454,6 +1476,12 @@ I after use>.\n\n" (see L and Eguestfs-structs.hE), or NULL if there was an error. I after use>.\n\n" + | RHashtable _ -> + pr "This function returns a NULL-terminated array of +strings, or NULL if there was an error. +The array of strings will always have length C<2n+1>, where +C keys and values alternate, followed by the trailing NULL entry. +I.\n\n" ); if List.mem ProtocolLimitWarning flags then pr "%s\n\n" protocol_limit_warning; @@ -1594,11 +1622,15 @@ and generate_xdr () = | RStat n -> pr "struct %s_ret {\n" name; pr " guestfs_int_stat %s;\n" n; - pr "};\n\n"; + pr "};\n\n" | RStatVFS n -> pr "struct %s_ret {\n" name; pr " guestfs_int_statvfs %s;\n" n; - pr "};\n\n"; + pr "};\n\n" + | RHashtable n -> + pr "struct %s_ret {\n" name; + pr " str %s<>;\n" n; + pr "};\n\n" ); ) daemon_functions; @@ -1744,7 +1776,8 @@ and generate_client_actions () = | RBool _ | RString _ | RStringList _ | RIntBool _ | RPVList _ | RVGList _ | RLVList _ - | RStat _ | RStatVFS _ -> + | RStat _ | RStatVFS _ + | RHashtable _ -> pr " struct %s_ret ret;\n" name ); pr "};\n\n"; @@ -1774,7 +1807,8 @@ and generate_client_actions () = | RBool _ | RString _ | RStringList _ | RIntBool _ | RPVList _ | RVGList _ | RLVList _ - | RStat _ | RStatVFS _ -> + | RStat _ | RStatVFS _ + | RHashtable _ -> pr " if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name; pr " error (g, \"%s: failed to parse reply\");\n" name; pr " return;\n"; @@ -1797,7 +1831,8 @@ and generate_client_actions () = failwithf "RConstString cannot be returned from a daemon function" | RString _ | RStringList _ | RIntBool _ | RPVList _ | RVGList _ | RLVList _ - | RStat _ | RStatVFS _ -> + | RStat _ | RStatVFS _ + | RHashtable _ -> "NULL" in pr "{\n"; @@ -1879,7 +1914,7 @@ and generate_client_actions () = failwithf "RConstString cannot be returned from a daemon function" | RString n -> pr " return rv.ret.%s; /* caller will free */\n" n - | RStringList n -> + | RStringList n | RHashtable n -> pr " /* caller will free this, but we need to add a NULL entry */\n"; pr " rv.ret.%s.%s_val =" n n; pr " safe_realloc (g, rv.ret.%s.%s_val,\n" n n; @@ -1890,19 +1925,8 @@ and generate_client_actions () = | RIntBool _ -> pr " /* caller with free this */\n"; pr " return safe_memdup (g, &rv.ret, sizeof (rv.ret));\n" - | RPVList n -> - pr " /* caller will free this */\n"; - pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n - | RVGList n -> - pr " /* caller will free this */\n"; - pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n - | 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 -> + | RPVList n | RVGList n | RLVList n + | RStat n | RStatVFS n -> pr " /* caller will free this */\n"; pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n ); @@ -1955,7 +1979,7 @@ and generate_daemon_actions () = | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" | RString _ -> pr " char *r;\n"; "NULL" - | RStringList _ -> pr " char **r;\n"; "NULL" + | RStringList _ | RHashtable _ -> pr " char **r;\n"; "NULL" | 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" @@ -2027,7 +2051,7 @@ and generate_daemon_actions () = pr " ret.%s = r;\n" n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name; pr " free (r);\n" - | RStringList n -> + | RStringList n | RHashtable n -> pr " struct guestfs_%s_ret ret;\n" name; pr " ret.%s.%s_len = count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; @@ -2036,7 +2060,8 @@ 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 | RVGList n | RLVList n | RStat n | RStatVFS 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; @@ -2270,6 +2295,14 @@ static void print_strings (char * const * const argv) printf (\"\\t%%s\\n\", argv[argc]); } +static void print_table (char * const * const argv) +{ + int i; + + for (i = 0; argv[i] != NULL; i += 2) + printf (\"%%s: %%s\\n\", argv[i], argv[i+1]); +} + "; let test_names = @@ -2660,7 +2693,7 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | RErr | RInt _ | RBool _ -> pr " int r;\n"; "-1" | RConstString _ -> pr " const char *r;\n"; "NULL" | RString _ -> pr " char *r;\n"; "NULL" - | RStringList _ -> + | RStringList _ | RHashtable _ -> pr " char **r;\n"; pr " int i;\n"; "NULL" @@ -2714,7 +2747,7 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = (match fst style with | RErr | RInt _ | RBool _ | RConstString _ -> () | RString _ -> pr " free (r);\n" - | RStringList _ -> + | RStringList _ | RHashtable _ -> pr " for (i = 0; r[i] != NULL; ++i)\n"; pr " free (r[i]);\n"; pr " free (r);\n" @@ -2893,7 +2926,7 @@ and generate_fish_cmds () = | RBool _ -> pr " int r;\n" | RConstString _ -> pr " const char *r;\n" | RString _ -> pr " char *r;\n" - | RStringList _ -> pr " char **r;\n" + | RStringList _ | RHashtable _ -> pr " char **r;\n" | RIntBool _ -> pr " struct guestfs_int_bool *r;\n" | RPVList _ -> pr " struct guestfs_lvm_pv_list *r;\n" | RVGList _ -> pr " struct guestfs_lvm_vg_list *r;\n" @@ -2997,6 +3030,11 @@ and generate_fish_cmds () = pr " print_statvfs (r);\n"; pr " free (r);\n"; pr " return 0;\n" + | RHashtable _ -> + pr " if (r == NULL) return -1;\n"; + pr " print_table (r);\n"; + pr " free_strings (r);\n"; + pr " return 0;\n" ); pr "}\n"; pr "\n" @@ -3163,7 +3201,7 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) | RBool _ -> pr "int " | RConstString _ -> pr "const char *" | RString _ -> pr "char *" - | RStringList _ -> pr "char **" + | RStringList _ | RHashtable _ -> pr "char **" | RIntBool _ -> if not in_daemon then pr "struct guestfs_int_bool *" else pr "guestfs_%s_ret *" name @@ -3296,22 +3334,51 @@ let () = and generate_ocaml_c () = generate_header CStyle LGPLv2; - pr "#include \n"; - pr "#include \n"; - pr "#include \n"; - pr "\n"; - pr "#include \n"; - pr "#include \n"; - pr "#include \n"; - pr "#include \n"; - pr "#include \n"; - pr "#include \n"; - pr "#include \n"; - pr "\n"; - pr "#include \n"; - pr "\n"; - pr "#include \"guestfs_c.h\"\n"; - pr "\n"; + pr "\ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include \"guestfs_c.h\" + +/* Copy a hashtable of string pairs into an assoc-list. We return + * the list in reverse order, but hashtables aren't supposed to be + * ordered anyway. + */ +static CAMLprim value +copy_table (char * const * argv) +{ + CAMLparam0 (); + CAMLlocal5 (rv, pairv, kv, vv, cons); + int i; + + rv = Val_int (0); + for (i = 0; argv[i] != NULL; i += 2) { + kv = caml_copy_string (argv[i]); + vv = caml_copy_string (argv[i+1]); + pairv = caml_alloc (2, 0); + Store_field (pairv, 0, kv); + Store_field (pairv, 1, vv); + cons = caml_alloc (2, 0); + Store_field (cons, 1, rv); + rv = cons; + Store_field (cons, 0, pairv); + } + + CAMLreturn (rv); +} + +"; (* LVM struct copy functions. *) List.iter ( @@ -3464,7 +3531,10 @@ and generate_ocaml_c () = | RStat _ -> pr " struct guestfs_stat *r;\n"; "NULL" | RStatVFS _ -> - pr " struct guestfs_statvfs *r;\n"; "NULL" in + pr " struct guestfs_statvfs *r;\n"; "NULL" + | RHashtable _ -> + pr " char **r;\n"; + "NULL" in pr "\n"; pr " caml_enter_blocking_section ();\n"; @@ -3516,6 +3586,9 @@ and generate_ocaml_c () = | RStatVFS _ -> pr " rv = copy_statvfs (r);\n"; pr " free (r);\n"; + | RHashtable _ -> + pr " rv = copy_table (r);\n"; + pr " free (r);\n"; ); pr " CAMLreturn (rv);\n"; @@ -3586,6 +3659,7 @@ and generate_ocaml_prototype ?(is_external = false) name style = | RLVList _ -> pr "lvm_lv array" | RStat _ -> pr "stat" | RStatVFS _ -> pr "statvfs" + | RHashtable _ -> pr "(string * string) list" ); if is_external then ( pr " = "; @@ -3697,7 +3771,8 @@ DESTROY (g) | RStringList _ | RIntBool _ | RPVList _ | RVGList _ | RLVList _ - | RStat _ | RStatVFS _ -> + | RStat _ | RStatVFS _ + | RHashtable _ -> pr "void\n" (* all lists returned implictly on the stack *) ); (* Call and arguments. *) @@ -3778,7 +3853,7 @@ DESTROY (g) pr " free (%s);\n" n; pr " OUTPUT:\n"; pr " RETVAL\n" - | RStringList n -> + | RStringList n | RHashtable n -> pr "PREINIT:\n"; pr " char **%s;\n" n; pr " int i, n;\n"; @@ -4013,7 +4088,8 @@ and generate_perl_prototype name style = | RVGList n | RLVList n -> pr "@%s = " n | RStat n - | RStatVFS n -> pr "%%%s = " n + | RStatVFS n + | RHashtable n -> pr "%%%s = " n ); pr "$h->%s (" name; let comma = ref false in @@ -4107,6 +4183,27 @@ put_string_list (char * const * const argv) return list; } +static PyObject * +put_table (char * const * const argv) +{ + PyObject *list, *item; + int argc, i; + + for (argc = 0; argv[argc] != NULL; ++argc) + ; + + list = PyList_New (argc >> 1); + for (i = 0; i < argc; i += 2) { + PyObject *item; + item = PyTuple_New (2); + PyTuple_SetItem (item, 0, PyString_FromString (argv[i])); + PyTuple_SetItem (item, 1, PyString_FromString (argv[i+1])); + PyList_SetItem (list, i >> 1, item); + } + + return list; +} + static void free_strings (char **argv) { @@ -4242,7 +4339,7 @@ py_guestfs_close (PyObject *self, PyObject *args) | 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"; "NULL" + | RStringList _ | RHashtable _ -> pr " char **r;\n"; "NULL" | 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" @@ -4348,6 +4445,9 @@ py_guestfs_close (PyObject *self, PyObject *args) | RStatVFS n -> pr " py_r = put_statvfs (r);\n"; pr " free (r);\n" + | RHashtable n -> + pr " py_r = put_table (r);\n"; + pr " free (r);\n" ); pr " return py_r;\n"; -- 1.8.3.1