diff options
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | daemon/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/actions.h | 30 | ||||
-rw-r--r-- | daemon/augeas.c | 278 | ||||
-rw-r--r-- | daemon/configure.ac | 7 | ||||
-rw-r--r-- | daemon/stubs.c | 316 | ||||
-rw-r--r-- | fish/cmds.c | 276 | ||||
-rw-r--r-- | guestfish-actions.pod | 152 | ||||
-rw-r--r-- | guestfs-actions.pod | 203 | ||||
-rw-r--r-- | libguestfs.spec.in | 3 | ||||
-rwxr-xr-x | make-initramfs.sh.in | 4 | ||||
-rw-r--r-- | ocaml/guestfs.ml | 12 | ||||
-rw-r--r-- | ocaml/guestfs.mli | 36 | ||||
-rw-r--r-- | ocaml/guestfs_c_actions.c | 293 | ||||
-rw-r--r-- | perl/Guestfs.xs | 138 | ||||
-rw-r--r-- | perl/lib/Sys/Guestfs.pm | 128 | ||||
-rwxr-xr-x | src/generator.ml | 410 | ||||
-rw-r--r-- | src/guestfs-actions.c | 862 | ||||
-rw-r--r-- | src/guestfs-actions.h | 12 | ||||
-rw-r--r-- | src/guestfs-structs.h | 5 | ||||
-rw-r--r-- | src/guestfs.c | 6 | ||||
-rw-r--r-- | src/guestfs.h | 1 | ||||
-rw-r--r-- | src/guestfs_protocol.c | 159 | ||||
-rw-r--r-- | src/guestfs_protocol.h | 124 | ||||
-rw-r--r-- | src/guestfs_protocol.x | 107 |
26 files changed, 3441 insertions, 138 deletions
@@ -35,6 +35,8 @@ Requirements - XDR, rpcgen +- Augeas (http://augeas.net/) + - perldoc (pod2man, pod2text) to generate the manual pages and other documentation. diff --git a/configure.ac b/configure.ac index 0e1df677..7c533eb5 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[0.6]) +AC_INIT([libguestfs],[0.7]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) @@ -90,6 +90,18 @@ AC_DEFINE_UNQUOTED([REPO],["$REPO"],[Name of Fedora repository.]) AC_DEFINE_UNQUOTED([host_cpu],["$host_cpu"],[Host architecture.]) +dnl --with-updates to specify a Fedora updates repository. +AC_ARG_WITH([updates], + [AS_HELP_STRING([--with-updates], + [set name of Fedora updates repository @<:@default=updates-released-f10@:>@])], + [], + [with_updates=updates-released-f10]) +UPDATES="$with_updates" +AC_SUBST(UPDATES) +AC_DEFINE_UNQUOTED([UPDATES],["$UPDATES"],[Name of Fedora updates repository.]) + +AC_DEFINE_UNQUOTED([host_cpu],["$host_cpu"],[Host architecture.]) + dnl --with-mirror to specify a local Fedora mirror. AC_ARG_WITH([mirror], [AS_HELP_STRING([--with-mirror], diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 4d56034d..bdeeacfe 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -20,6 +20,7 @@ ACLOCAL_AMFLAGS = -I m4 noinst_PROGRAMS = guestfsd guestfsd_SOURCES = \ actions.h \ + augeas.c \ daemon.h \ devsparts.c \ file.c \ diff --git a/daemon/actions.h b/daemon/actions.h index 01fe68d6..6a41c7d0 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -22,17 +22,29 @@ #include "../src/guestfs_protocol.h" extern int do_mount (const char *device, const char *mountpoint); -extern int do_sync (); +extern int do_sync (void); extern int do_touch (const char *path); extern char *do_cat (const char *path); extern char *do_ll (const char *directory); extern char **do_ls (const char *directory); -extern char **do_list_devices (); -extern char **do_list_partitions (); -extern char **do_pvs (); -extern char **do_vgs (); -extern char **do_lvs (); -extern guestfs_lvm_int_pv_list *do_pvs_full (); -extern guestfs_lvm_int_vg_list *do_vgs_full (); -extern guestfs_lvm_int_lv_list *do_lvs_full (); +extern char **do_list_devices (void); +extern char **do_list_partitions (void); +extern char **do_pvs (void); +extern char **do_vgs (void); +extern char **do_lvs (void); +extern guestfs_lvm_int_pv_list *do_pvs_full (void); +extern guestfs_lvm_int_vg_list *do_vgs_full (void); +extern guestfs_lvm_int_lv_list *do_lvs_full (void); extern char **do_read_lines (const char *path); +extern int do_aug_init (const char *root, int flags); +extern int do_aug_close (void); +extern int do_aug_defvar (const char *name, const char *expr); +extern guestfs_aug_defnode_ret *do_aug_defnode (const char *name, const char *expr, const char *val); +extern char *do_aug_get (const char *path); +extern int do_aug_set (const char *path, const char *val); +extern int do_aug_insert (const char *path, const char *label, int before); +extern int do_aug_rm (const char *path); +extern int do_aug_mv (const char *src, const char *dest); +extern char **do_aug_match (const char *path); +extern int do_aug_save (void); +extern int do_aug_load (void); diff --git a/daemon/augeas.c b/daemon/augeas.c new file mode 100644 index 00000000..2b273875 --- /dev/null +++ b/daemon/augeas.c @@ -0,0 +1,278 @@ +/* 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 <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <augeas.h> + +#include "daemon.h" +#include "actions.h" + +/* The Augeas handle. We maintain a single handle per daemon, which + * is all that is necessary and reduces the complexity of the API + * considerably. + */ +static augeas *aug = NULL; + +#define NEED_AUG(errcode) \ + do { \ + if (!aug) { \ + reply_with_error ("%s: you must call 'aug-init' first to initialize Augeas", __func__); \ + return (errcode); \ + } \ + } \ + while (0) + +/* We need to rewrite the root path so it is based at /sysroot. */ +int +do_aug_init (const char *root, int flags) +{ + char *buf; + int len; + + NEED_ROOT (-1); + ABS_PATH (root, -1); + + if (aug) { + aug_close (aug); + aug = NULL; + } + + len = strlen (root) + 8; + buf = malloc (len); + if (!buf) { + reply_with_perror ("malloc"); + return -1; + } + snprintf (buf, len, "/sysroot%s", root); + + aug = aug_init (buf, NULL, flags); + free (buf); + + if (!aug) { + reply_with_error ("Augeas initialization failed"); + return -1; + } + + return 0; +} + +int +do_aug_close (void) +{ + NEED_AUG(-1); + + aug_close (aug); + aug = NULL; + + return 0; +} + +int +do_aug_defvar (const char *name, const char *expr) +{ + int r; + + NEED_AUG (-1); + + r = aug_defvar (aug, name, expr); + if (r == -1) { + reply_with_error ("Augeas defvar failed"); + return -1; + } + return r; +} + +guestfs_aug_defnode_ret * +do_aug_defnode (const char *name, const char *expr, const char *val) +{ + static guestfs_aug_defnode_ret r; + int created; + + NEED_AUG (NULL); + + r.nrnodes = aug_defnode (aug, name, expr, val, &created); + if (r.nrnodes == -1) { + reply_with_error ("Augeas defnode failed"); + return NULL; + } + r.created = created; + return &r; +} + +char * +do_aug_get (const char *path) +{ + const char *value = NULL; + char *v; + int r; + + NEED_AUG (NULL); + + r = aug_get (aug, path, &value); + if (r == 0) { + reply_with_error ("no matching node"); + return NULL; + } + if (r != 1) { + reply_with_error ("Augeas get failed"); + return NULL; + } + + /* value can still be NULL here, eg. try with path == "/augeas". + * I don't understand this case, and it seems to contradict the + * documentation. + */ + if (value == NULL) { + reply_with_error ("Augeas returned NULL match"); + return NULL; + } + + /* The value is an internal Augeas string, so we must copy it. GC FTW. */ + v = strdup (value); + if (v == NULL) { + reply_with_perror ("strdup"); + return NULL; + } + + return v; /* Caller frees. */ +} + +int +do_aug_set (const char *path, const char *val) +{ + int r; + + NEED_AUG (-1); + + r = aug_set (aug, path, val); + if (r == -1) { + reply_with_error ("Augeas set failed"); + return -1; + } + + return 0; +} + +int +do_aug_insert (const char *path, const char *label, int before) +{ + int r; + + NEED_AUG (-1); + + r = aug_insert (aug, path, label, before); + if (r == -1) { + reply_with_error ("Augeas insert failed"); + return -1; + } + + return 0; +} + +int +do_aug_rm (const char *path) +{ + int r; + + NEED_AUG (-1); + + r = aug_rm (aug, path); + if (r == -1) { + reply_with_error ("Augeas rm failed"); + return -1; + } + + return r; +} + +int +do_aug_mv (const char *src, const char *dest) +{ + int r; + + NEED_AUG (-1); + + r = aug_mv (aug, src, dest); + if (r == -1) { + reply_with_error ("Augeas mv failed"); + return -1; + } + + return 0; +} + +char ** +do_aug_match (const char *path) +{ + char **matches = NULL; + void *vp; + int r; + + NEED_AUG (NULL); + + r = aug_match (aug, path, &matches); + if (r == -1) { + reply_with_error ("Augeas match failed"); + return NULL; + } + + /* This returns an array of length r, which we must extend + * and add a terminating NULL. + */ + vp = realloc (matches, sizeof (char *) * (r+1)); + if (vp == NULL) { + reply_with_perror ("realloc"); + free (vp); + return NULL; + } + matches = vp; + matches[r] = NULL; + + return matches; /* Caller frees. */ +} + +int +do_aug_save (void) +{ + NEED_AUG (-1); + + if (aug_save (aug) == -1) { + reply_with_error ("Augeas save failed"); + return -1; + } + + return 0; +} + +int +do_aug_load (void) +{ + NEED_AUG (-1); + + if (aug_load (aug) == -1) { + reply_with_error ("Augeas load failed"); + return -1; + } + + return 0; +} diff --git a/daemon/configure.ac b/daemon/configure.ac index af0a78af..7ce03ab5 100644 --- a/daemon/configure.ac +++ b/daemon/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs-daemon],[0.2]) +AC_INIT([libguestfs-daemon],[0.7]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) @@ -37,6 +37,11 @@ test "x$U" != "x" && AC_MSG_ERROR([Compiler not ANSI compliant]) AC_PROG_CC_C_O +dnl Check for Augeas. +AC_CHECK_LIB([augeas],[aug_match],,[ + AC_MSG_ERROR([Augeas library not found]) + ]) + dnl Check for XDR library. AC_CHECK_LIB([portablexdr],[xdrmem_create],[],[ AC_SEARCH_LIBS([xdrmem_create],[rpc xdr nsl]) diff --git a/daemon/stubs.c b/daemon/stubs.c index fab115c0..38aea33f 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -43,7 +43,7 @@ static void mount_stub (XDR *xdr_in) memset (&args, 0, sizeof args); if (!xdr_guestfs_mount_args (xdr_in, &args)) { - reply_with_error ("mount: daemon failed to decode procedure arguments"); + reply_with_error ("%s: daemon failed to decode procedure arguments", "mount"); return; } device = args.device; @@ -78,7 +78,7 @@ static void touch_stub (XDR *xdr_in) memset (&args, 0, sizeof args); if (!xdr_guestfs_touch_args (xdr_in, &args)) { - reply_with_error ("touch: daemon failed to decode procedure arguments"); + reply_with_error ("%s: daemon failed to decode procedure arguments", "touch"); return; } path = args.path; @@ -100,7 +100,7 @@ static void cat_stub (XDR *xdr_in) memset (&args, 0, sizeof args); if (!xdr_guestfs_cat_args (xdr_in, &args)) { - reply_with_error ("cat: daemon failed to decode procedure arguments"); + reply_with_error ("%s: daemon failed to decode procedure arguments", "cat"); return; } path = args.path; @@ -125,7 +125,7 @@ static void ll_stub (XDR *xdr_in) memset (&args, 0, sizeof args); if (!xdr_guestfs_ll_args (xdr_in, &args)) { - reply_with_error ("ll: daemon failed to decode procedure arguments"); + reply_with_error ("%s: daemon failed to decode procedure arguments", "ll"); return; } directory = args.directory; @@ -150,7 +150,7 @@ static void ls_stub (XDR *xdr_in) memset (&args, 0, sizeof args); if (!xdr_guestfs_ls_args (xdr_in, &args)) { - reply_with_error ("ls: daemon failed to decode procedure arguments"); + reply_with_error ("%s: daemon failed to decode procedure arguments", "ls"); return; } directory = args.directory; @@ -258,7 +258,7 @@ static void pvs_full_stub (XDR *xdr_in) struct guestfs_pvs_full_ret ret; ret.physvols = *r; - reply ((xdrproc_t) &xdr_guestfs_pvs_full_ret, (char *) &ret); + reply ((xdrproc_t) xdr_guestfs_pvs_full_ret, (char *) &ret); xdr_free ((xdrproc_t) xdr_guestfs_pvs_full_ret, (char *) &ret); } @@ -273,7 +273,7 @@ static void vgs_full_stub (XDR *xdr_in) struct guestfs_vgs_full_ret ret; ret.volgroups = *r; - reply ((xdrproc_t) &xdr_guestfs_vgs_full_ret, (char *) &ret); + reply ((xdrproc_t) xdr_guestfs_vgs_full_ret, (char *) &ret); xdr_free ((xdrproc_t) xdr_guestfs_vgs_full_ret, (char *) &ret); } @@ -288,7 +288,7 @@ static void lvs_full_stub (XDR *xdr_in) struct guestfs_lvs_full_ret ret; ret.logvols = *r; - reply ((xdrproc_t) &xdr_guestfs_lvs_full_ret, (char *) &ret); + reply ((xdrproc_t) xdr_guestfs_lvs_full_ret, (char *) &ret); xdr_free ((xdrproc_t) xdr_guestfs_lvs_full_ret, (char *) &ret); } @@ -301,7 +301,7 @@ static void read_lines_stub (XDR *xdr_in) memset (&args, 0, sizeof args); if (!xdr_guestfs_read_lines_args (xdr_in, &args)) { - reply_with_error ("read_lines: daemon failed to decode procedure arguments"); + reply_with_error ("%s: daemon failed to decode procedure arguments", "read_lines"); return; } path = args.path; @@ -318,6 +318,268 @@ static void read_lines_stub (XDR *xdr_in) free_strings (r); } +static void aug_init_stub (XDR *xdr_in) +{ + int r; + struct guestfs_aug_init_args args; + const char *root; + int flags; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_init_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_init"); + return; + } + root = args.root; + flags = args.flags; + + r = do_aug_init (root, flags); + if (r == -1) + /* do_aug_init has already called reply_with_error, so just return */ + return; + + reply (NULL, NULL); +} + +static void aug_close_stub (XDR *xdr_in) +{ + int r; + + r = do_aug_close (); + if (r == -1) + /* do_aug_close has already called reply_with_error, so just return */ + return; + + reply (NULL, NULL); +} + +static void aug_defvar_stub (XDR *xdr_in) +{ + int r; + struct guestfs_aug_defvar_args args; + const char *name; + const char *expr; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_defvar_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_defvar"); + return; + } + name = args.name; + expr = args.expr ? *args.expr : NULL; + + r = do_aug_defvar (name, expr); + if (r == -1) + /* do_aug_defvar has already called reply_with_error, so just return */ + return; + + struct guestfs_aug_defvar_ret ret; + ret.nrnodes = r; + reply ((xdrproc_t) &xdr_guestfs_aug_defvar_ret, (char *) &ret); +} + +static void aug_defnode_stub (XDR *xdr_in) +{ + guestfs_aug_defnode_ret *r; + struct guestfs_aug_defnode_args args; + const char *name; + const char *expr; + const char *val; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_defnode_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_defnode"); + return; + } + name = args.name; + expr = args.expr; + val = args.val; + + r = do_aug_defnode (name, expr, val); + if (r == NULL) + /* do_aug_defnode has already called reply_with_error, so just return */ + return; + + reply ((xdrproc_t) xdr_guestfs_aug_defnode_ret, (char *) r); + xdr_free ((xdrproc_t) xdr_guestfs_aug_defnode_ret, (char *) r); +} + +static void aug_get_stub (XDR *xdr_in) +{ + char *r; + struct guestfs_aug_get_args args; + const char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_get_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_get"); + return; + } + path = args.path; + + r = do_aug_get (path); + if (r == NULL) + /* do_aug_get has already called reply_with_error, so just return */ + return; + + struct guestfs_aug_get_ret ret; + ret.val = r; + reply ((xdrproc_t) &xdr_guestfs_aug_get_ret, (char *) &ret); + free (r); +} + +static void aug_set_stub (XDR *xdr_in) +{ + int r; + struct guestfs_aug_set_args args; + const char *path; + const char *val; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_set_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_set"); + return; + } + path = args.path; + val = args.val; + + r = do_aug_set (path, val); + if (r == -1) + /* do_aug_set has already called reply_with_error, so just return */ + return; + + reply (NULL, NULL); +} + +static void aug_insert_stub (XDR *xdr_in) +{ + int r; + struct guestfs_aug_insert_args args; + const char *path; + const char *label; + int before; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_insert_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_insert"); + return; + } + path = args.path; + label = args.label; + before = args.before; + + r = do_aug_insert (path, label, before); + if (r == -1) + /* do_aug_insert has already called reply_with_error, so just return */ + return; + + reply (NULL, NULL); +} + +static void aug_rm_stub (XDR *xdr_in) +{ + int r; + struct guestfs_aug_rm_args args; + const char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_rm_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_rm"); + return; + } + path = args.path; + + r = do_aug_rm (path); + if (r == -1) + /* do_aug_rm has already called reply_with_error, so just return */ + return; + + struct guestfs_aug_rm_ret ret; + ret.nrnodes = r; + reply ((xdrproc_t) &xdr_guestfs_aug_rm_ret, (char *) &ret); +} + +static void aug_mv_stub (XDR *xdr_in) +{ + int r; + struct guestfs_aug_mv_args args; + const char *src; + const char *dest; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_mv_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_mv"); + return; + } + src = args.src; + dest = args.dest; + + r = do_aug_mv (src, dest); + if (r == -1) + /* do_aug_mv has already called reply_with_error, so just return */ + return; + + reply (NULL, NULL); +} + +static void aug_match_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_aug_match_args args; + const char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_aug_match_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "aug_match"); + return; + } + path = args.path; + + r = do_aug_match (path); + if (r == NULL) + /* do_aug_match has already called reply_with_error, so just return */ + return; + + struct guestfs_aug_match_ret ret; + ret.matches.matches_len = count_strings (r); + ret.matches.matches_val = r; + reply ((xdrproc_t) &xdr_guestfs_aug_match_ret, (char *) &ret); + free_strings (r); +} + +static void aug_save_stub (XDR *xdr_in) +{ + int r; + + r = do_aug_save (); + if (r == -1) + /* do_aug_save has already called reply_with_error, so just return */ + return; + + reply (NULL, NULL); +} + +static void aug_load_stub (XDR *xdr_in) +{ + int r; + + r = do_aug_load (); + if (r == -1) + /* do_aug_load has already called reply_with_error, so just return */ + return; + + reply (NULL, NULL); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -366,6 +628,42 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_READ_LINES: read_lines_stub (xdr_in); break; + case GUESTFS_PROC_AUG_INIT: + aug_init_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_CLOSE: + aug_close_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_DEFVAR: + aug_defvar_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_DEFNODE: + aug_defnode_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_GET: + aug_get_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_SET: + aug_set_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_INSERT: + aug_insert_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_RM: + aug_rm_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_MV: + aug_mv_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_MATCH: + aug_match_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_SAVE: + aug_save_stub (xdr_in); + break; + case GUESTFS_PROC_AUG_LOAD: + aug_load_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index aaf97a89..5c7d5530 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -33,6 +33,18 @@ void list_commands (void) list_builtin_commands (); printf ("%-20s %s\n", "add-cdrom", "add a CD-ROM disk image to examine"); printf ("%-20s %s\n", "add-drive", "add an image to examine or modify"); + printf ("%-20s %s\n", "aug-close", "close the current Augeas handle"); + printf ("%-20s %s\n", "aug-defnode", "define an Augeas node"); + printf ("%-20s %s\n", "aug-defvar", "define an Augeas variable"); + printf ("%-20s %s\n", "aug-get", "look up the value of an Augeas path"); + printf ("%-20s %s\n", "aug-init", "create a new Augeas handle"); + printf ("%-20s %s\n", "aug-insert", "insert a sibling Augeas node"); + printf ("%-20s %s\n", "aug-load", "load files into the tree"); + printf ("%-20s %s\n", "aug-match", "return Augeas nodes which match path"); + printf ("%-20s %s\n", "aug-mv", "move Augeas node"); + printf ("%-20s %s\n", "aug-rm", "remove an Augeas path"); + printf ("%-20s %s\n", "aug-save", "write all pending Augeas changes to disk"); + printf ("%-20s %s\n", "aug-set", "set Augeas path to value"); printf ("%-20s %s\n", "cat", "list the contents of a file"); printf ("%-20s %s\n", "config", "add qemu parameters"); printf ("%-20s %s\n", "get-autosync", "get autosync mode"); @@ -140,6 +152,42 @@ void display_command (const char *cmd) if (strcasecmp (cmd, "read_lines") == 0 || strcasecmp (cmd, "read-lines") == 0) pod2text ("read-lines - read file as lines", " read-lines <path>\n\nReturn the contents of the file named C<path>.\n\nThe file contents are returned as a list of lines. Trailing\nC<LF> and C<CRLF> character sequences are I<not> returned.\n\nNote that this function cannot correctly handle binary files\n(specifically, files containing C<\\0> character which is treated\nas end of line). For those you need to use the C<read_file>\nfunction which has a more complex interface."); else + if (strcasecmp (cmd, "aug_init") == 0 || strcasecmp (cmd, "aug-init") == 0) + pod2text ("aug-init - create a new Augeas handle", " aug-init <root> <flags>\n\nCreate a new Augeas handle for editing configuration files.\nIf there was any previous Augeas handle associated with this\nguestfs session, then it is closed.\n\nYou must call this before using any other C<aug_*>\ncommands.\n\nC<root> is the filesystem root. C<root> must not be NULL,\nuse C</> instead.\n\nThe flags are the same as the flags defined in\nE<lt>augeas.hE<gt>, the logical I<or> of the following\nintegers:\n\n=over 4\n\n=item 1 C<AUG_SAVE_BACKUP>\n\nKeep the original file with a C<.augsave> extension.\n\n=item 2 C<AUG_SAVE_NEWFILE>\n\nSave changes into a file with extension C<.augnew>, and\ndo not overwrite original. Overrides C<AUG_SAVE_BACKUP>.\n\n=item 4 C<AUG_TYPE_CHECK>\n\nTypecheck lenses (can be expensive).\n\n=item 8 C<AUG_NO_STDINC>\n\nDo not use standard load path for modules.\n\n=item 16 C<AUG_SAVE_NOOP>\n\nMake save a no-op, just record what would have been changed.\n\n=item 32 C<AUG_NO_LOAD>\n\nDo not load the tree in C<aug_init>.\n\n=back\n\nTo close the handle, you can call C<aug_close>.\n\nTo find out more about Augeas, see L<http://augeas.net/>."); + else + if (strcasecmp (cmd, "aug_close") == 0 || strcasecmp (cmd, "aug-close") == 0) + pod2text ("aug-close - close the current Augeas handle", " aug-close\n\nClose the current Augeas handle and free up any resources\nused by it. After calling this, you have to call\nC<aug_init> again before you can use any other\nAugeas functions."); + else + if (strcasecmp (cmd, "aug_defvar") == 0 || strcasecmp (cmd, "aug-defvar") == 0) + pod2text ("aug-defvar - define an Augeas variable", " aug-defvar <name> <expr>\n\nDefines an Augeas variable C<name> whose value is the result\nof evaluating C<expr>. If C<expr> is NULL, then C<name> is\nundefined.\n\nOn success this returns the number of nodes in C<expr>, or\nC<0> if C<expr> evaluates to something which is not a nodeset."); + else + if (strcasecmp (cmd, "aug_defnode") == 0 || strcasecmp (cmd, "aug-defnode") == 0) + pod2text ("aug-defnode - define an Augeas node", " aug-defnode <name> <expr> <val>\n\nDefines a variable C<name> whose value is the result of\nevaluating C<expr>.\n\nIf C<expr> evaluates to an empty nodeset, a node is created,\nequivalent to calling C<aug_set> C<expr>, C<value>.\nC<name> will be the nodeset containing that single node.\n\nOn success this returns a pair containing the\nnumber of nodes in the nodeset, and a boolean flag\nif a node was created."); + else + if (strcasecmp (cmd, "aug_get") == 0 || strcasecmp (cmd, "aug-get") == 0) + pod2text ("aug-get - look up the value of an Augeas path", " aug-get <path>\n\nLook up the value associated with C<path>. If C<path>\nmatches exactly one node, the C<value> is returned."); + else + if (strcasecmp (cmd, "aug_set") == 0 || strcasecmp (cmd, "aug-set") == 0) + pod2text ("aug-set - set Augeas path to value", " aug-set <path> <val>\n\nSet the value associated with C<path> to C<value>."); + else + if (strcasecmp (cmd, "aug_insert") == 0 || strcasecmp (cmd, "aug-insert") == 0) + pod2text ("aug-insert - insert a sibling Augeas node", " aug-insert <path> <label> <before>\n\nCreate a new sibling C<label> for C<path>, inserting it into\nthe tree before or after C<path> (depending on the boolean\nflag C<before>).\n\nC<path> must match exactly one existing node in the tree, and\nC<label> must be a label, ie. not contain C</>, C<*> or end\nwith a bracketed index C<[N]>."); + else + if (strcasecmp (cmd, "aug_rm") == 0 || strcasecmp (cmd, "aug-rm") == 0) + pod2text ("aug-rm - remove an Augeas path", " aug-rm <path>\n\nRemove C<path> and all of its children.\n\nOn success this returns the number of entries which were removed."); + else + if (strcasecmp (cmd, "aug_mv") == 0 || strcasecmp (cmd, "aug-mv") == 0) + pod2text ("aug-mv - move Augeas node", " aug-mv <src> <dest>\n\nMove the node C<src> to C<dest>. C<src> must match exactly\none node. C<dest> is overwritten if it exists."); + else + if (strcasecmp (cmd, "aug_match") == 0 || strcasecmp (cmd, "aug-match") == 0) + pod2text ("aug-match - return Augeas nodes which match path", " aug-match <path>\n\nReturns a list of paths which match the path expression C<path>.\nThe returned paths are sufficiently qualified so that they match\nexactly one node in the current tree."); + else + if (strcasecmp (cmd, "aug_save") == 0 || strcasecmp (cmd, "aug-save") == 0) + pod2text ("aug-save - write all pending Augeas changes to disk", " aug-save\n\nThis writes all pending changes to disk.\n\nThe flags which were passed to C<aug_init> affect exactly\nhow files are saved."); + else + if (strcasecmp (cmd, "aug_load") == 0 || strcasecmp (cmd, "aug-load") == 0) + pod2text ("aug-load - load files into the tree", " aug-load\n\nLoad files into the tree.\n\nSee C<aug_load> in the Augeas documentation for the full gory\ndetails."); + else display_builtin_command (cmd); } @@ -627,6 +675,198 @@ static int run_read_lines (const char *cmd, int argc, char *argv[]) return 0; } +static int run_aug_init (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *root; + int flags; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + root = argv[0]; + flags = atoi (argv[1]); + r = guestfs_aug_init (g, root, flags); + return r; +} + +static int run_aug_close (const char *cmd, int argc, char *argv[]) +{ + int r; + if (argc != 0) { + fprintf (stderr, "%s should have 0 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + r = guestfs_aug_close (g); + return r; +} + +static int run_aug_defvar (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *name; + const char *expr; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + name = argv[0]; + expr = strcmp (argv[1], "") != 0 ? argv[1] : NULL; + r = guestfs_aug_defvar (g, name, expr); + if (r == -1) return -1; + if (r) printf ("%d\n", r); + return 0; +} + +static int run_aug_defnode (const char *cmd, int argc, char *argv[]) +{ + struct guestfs_int_bool *r; + const char *name; + const char *expr; + const char *val; + if (argc != 3) { + fprintf (stderr, "%s should have 3 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + name = argv[0]; + expr = argv[1]; + val = argv[2]; + r = guestfs_aug_defnode (g, name, expr, val); + if (r == NULL) return -1; + printf ("%d, %s\n", r->i, + r->b ? "true" : "false"); + guestfs_free_int_bool (r); + return 0; +} + +static int run_aug_get (const char *cmd, int argc, char *argv[]) +{ + char *r; + const char *path; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + path = argv[0]; + r = guestfs_aug_get (g, path); + if (r == NULL) return -1; + printf ("%s\n", r); + free (r); + return 0; +} + +static int run_aug_set (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *path; + const char *val; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + path = argv[0]; + val = argv[1]; + r = guestfs_aug_set (g, path, val); + return r; +} + +static int run_aug_insert (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *path; + const char *label; + int before; + if (argc != 3) { + fprintf (stderr, "%s should have 3 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + path = argv[0]; + label = argv[1]; + before = is_true (argv[2]) ? 1 : 0; + r = guestfs_aug_insert (g, path, label, before); + return r; +} + +static int run_aug_rm (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *path; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + path = argv[0]; + r = guestfs_aug_rm (g, path); + if (r == -1) return -1; + if (r) printf ("%d\n", r); + return 0; +} + +static int run_aug_mv (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *src; + const char *dest; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + src = argv[0]; + dest = argv[1]; + r = guestfs_aug_mv (g, src, dest); + return r; +} + +static int run_aug_match (const char *cmd, int argc, char *argv[]) +{ + char **r; + const char *path; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + path = argv[0]; + r = guestfs_aug_match (g, path); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + +static int run_aug_save (const char *cmd, int argc, char *argv[]) +{ + int r; + if (argc != 0) { + fprintf (stderr, "%s should have 0 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + r = guestfs_aug_save (g); + return r; +} + +static int run_aug_load (const char *cmd, int argc, char *argv[]) +{ + int r; + if (argc != 0) { + fprintf (stderr, "%s should have 0 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + r = guestfs_aug_load (g); + return r; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -707,6 +947,42 @@ int run_action (const char *cmd, int argc, char *argv[]) if (strcasecmp (cmd, "read_lines") == 0 || strcasecmp (cmd, "read-lines") == 0) return run_read_lines (cmd, argc, argv); else + if (strcasecmp (cmd, "aug_init") == 0 || strcasecmp (cmd, "aug-init") == 0) + return run_aug_init (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_close") == 0 || strcasecmp (cmd, "aug-close") == 0) + return run_aug_close (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_defvar") == 0 || strcasecmp (cmd, "aug-defvar") == 0) + return run_aug_defvar (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_defnode") == 0 || strcasecmp (cmd, "aug-defnode") == 0) + return run_aug_defnode (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_get") == 0 || strcasecmp (cmd, "aug-get") == 0) + return run_aug_get (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_set") == 0 || strcasecmp (cmd, "aug-set") == 0) + return run_aug_set (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_insert") == 0 || strcasecmp (cmd, "aug-insert") == 0) + return run_aug_insert (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_rm") == 0 || strcasecmp (cmd, "aug-rm") == 0) + return run_aug_rm (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_mv") == 0 || strcasecmp (cmd, "aug-mv") == 0) + return run_aug_mv (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_match") == 0 || strcasecmp (cmd, "aug-match") == 0) + return run_aug_match (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_save") == 0 || strcasecmp (cmd, "aug-save") == 0) + return run_aug_save (cmd, argc, argv); + else + if (strcasecmp (cmd, "aug_load") == 0 || strcasecmp (cmd, "aug-load") == 0) + return run_aug_load (cmd, argc, argv); + else { fprintf (stderr, "%s: unknown command\n", cmd); return -1; diff --git a/guestfish-actions.pod b/guestfish-actions.pod index 13e23ae5..00911dbe 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -23,6 +23,158 @@ image). This is equivalent to the qemu parameter C<-drive file=filename>. +=head2 aug-close + + aug-close + +Close the current Augeas handle and free up any resources +used by it. After calling this, you have to call +C<aug_init> again before you can use any other +Augeas functions. + +=head2 aug-defnode + + aug-defnode name expr val + +Defines a variable C<name> whose value is the result of +evaluating C<expr>. + +If C<expr> evaluates to an empty nodeset, a node is created, +equivalent to calling C<aug_set> C<expr>, C<value>. +C<name> will be the nodeset containing that single node. + +On success this returns a pair containing the +number of nodes in the nodeset, and a boolean flag +if a node was created. + +=head2 aug-defvar + + aug-defvar name expr + +Defines an Augeas variable C<name> whose value is the result +of evaluating C<expr>. If C<expr> is NULL, then C<name> is +undefined. + +On success this returns the number of nodes in C<expr>, or +C<0> if C<expr> evaluates to something which is not a nodeset. + +=head2 aug-get + + aug-get path + +Look up the value associated with C<path>. If C<path> +matches exactly one node, the C<value> is returned. + +=head2 aug-init + + aug-init root flags + +Create a new Augeas handle for editing configuration files. +If there was any previous Augeas handle associated with this +guestfs session, then it is closed. + +You must call this before using any other C<aug_*> +commands. + +C<root> is the filesystem root. C<root> must not be NULL, +use C</> instead. + +The flags are the same as the flags defined in +E<lt>augeas.hE<gt>, the logical I<or> of the following +integers: + +=over 4 + +=item 1 C<AUG_SAVE_BACKUP> + +Keep the original file with a C<.augsave> extension. + +=item 2 C<AUG_SAVE_NEWFILE> + +Save changes into a file with extension C<.augnew>, and +do not overwrite original. Overrides C<AUG_SAVE_BACKUP>. + +=item 4 C<AUG_TYPE_CHECK> + +Typecheck lenses (can be expensive). + +=item 8 C<AUG_NO_STDINC> + +Do not use standard load path for modules. + +=item 16 C<AUG_SAVE_NOOP> + +Make save a no-op, just record what would have been changed. + +=item 32 C<AUG_NO_LOAD> + +Do not load the tree in C<aug_init>. + +=back + +To close the handle, you can call C<aug_close>. + +To find out more about Augeas, see L<http://augeas.net/>. + +=head2 aug-insert + + aug-insert path label true|false + +Create a new sibling C<label> for C<path>, inserting it into +the tree before or after C<path> (depending on the boolean +flag C<before>). + +C<path> must match exactly one existing node in the tree, and +C<label> must be a label, ie. not contain C</>, C<*> or end +with a bracketed index C<[N]>. + +=head2 aug-load + + aug-load + +Load files into the tree. + +See C<aug_load> in the Augeas documentation for the full gory +details. + +=head2 aug-match + + aug-match path + +Returns a list of paths which match the path expression C<path>. +The returned paths are sufficiently qualified so that they match +exactly one node in the current tree. + +=head2 aug-mv + + aug-mv src dest + +Move the node C<src> to C<dest>. C<src> must match exactly +one node. C<dest> is overwritten if it exists. + +=head2 aug-rm + + aug-rm path + +Remove C<path> and all of its children. + +On success this returns the number of entries which were removed. + +=head2 aug-save + + aug-save + +This writes all pending changes to disk. + +The flags which were passed to C<aug_init> affect exactly +how files are saved. + +=head2 aug-set + + aug-set path val + +Set the value associated with C<path> to C<value>. + =head2 cat cat path diff --git a/guestfs-actions.pod b/guestfs-actions.pod index fd720f9b..c5fadcfc 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -29,6 +29,203 @@ This is equivalent to the qemu parameter C<-drive file=filename>. This function returns 0 on success or -1 on error. +=head2 guestfs_aug_close + + int guestfs_aug_close (guestfs_h *handle); + +Close the current Augeas handle and free up any resources +used by it. After calling this, you have to call +C<guestfs_aug_init> again before you can use any other +Augeas functions. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_aug_defnode + + struct guestfs_int_bool *guestfs_aug_defnode (guestfs_h *handle, + const char *name, + const char *expr, + const char *val); + +Defines a variable C<name> whose value is the result of +evaluating C<expr>. + +If C<expr> evaluates to an empty nodeset, a node is created, +equivalent to calling C<guestfs_aug_set> C<expr>, C<value>. +C<name> will be the nodeset containing that single node. + +On success this returns a pair containing the +number of nodes in the nodeset, and a boolean flag +if a node was created. + +This function returns a C<struct guestfs_int_bool *>. +I<The caller must call C<guestfs_free_int_bool> after use.>. + +=head2 guestfs_aug_defvar + + int guestfs_aug_defvar (guestfs_h *handle, + const char *name, + const char *expr); + +Defines an Augeas variable C<name> whose value is the result +of evaluating C<expr>. If C<expr> is NULL, then C<name> is +undefined. + +On success this returns the number of nodes in C<expr>, or +C<0> if C<expr> evaluates to something which is not a nodeset. + +On error this function returns -1. + +=head2 guestfs_aug_get + + char *guestfs_aug_get (guestfs_h *handle, + const char *path); + +Look up the value associated with C<path>. If C<path> +matches exactly one node, the C<value> is returned. + +This function returns a string or NULL on error. +I<The caller must free the returned string after use>. + +=head2 guestfs_aug_init + + int guestfs_aug_init (guestfs_h *handle, + const char *root, + int flags); + +Create a new Augeas handle for editing configuration files. +If there was any previous Augeas handle associated with this +guestfs session, then it is closed. + +You must call this before using any other C<guestfs_aug_*> +commands. + +C<root> is the filesystem root. C<root> must not be NULL, +use C</> instead. + +The flags are the same as the flags defined in +E<lt>augeas.hE<gt>, the logical I<or> of the following +integers: + +=over 4 + +=item 1 C<AUG_SAVE_BACKUP> + +Keep the original file with a C<.augsave> extension. + +=item 2 C<AUG_SAVE_NEWFILE> + +Save changes into a file with extension C<.augnew>, and +do not overwrite original. Overrides C<AUG_SAVE_BACKUP>. + +=item 4 C<AUG_TYPE_CHECK> + +Typecheck lenses (can be expensive). + +=item 8 C<AUG_NO_STDINC> + +Do not use standard load path for modules. + +=item 16 C<AUG_SAVE_NOOP> + +Make save a no-op, just record what would have been changed. + +=item 32 C<AUG_NO_LOAD> + +Do not load the tree in C<guestfs_aug_init>. + +=back + +To close the handle, you can call C<guestfs_aug_close>. + +To find out more about Augeas, see L<http://augeas.net/>. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_aug_insert + + int guestfs_aug_insert (guestfs_h *handle, + const char *path, + const char *label, + int before); + +Create a new sibling C<label> for C<path>, inserting it into +the tree before or after C<path> (depending on the boolean +flag C<before>). + +C<path> must match exactly one existing node in the tree, and +C<label> must be a label, ie. not contain C</>, C<*> or end +with a bracketed index C<[N]>. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_aug_load + + int guestfs_aug_load (guestfs_h *handle); + +Load files into the tree. + +See C<aug_load> in the Augeas documentation for the full gory +details. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_aug_match + + char **guestfs_aug_match (guestfs_h *handle, + const char *path); + +Returns a list of paths which match the path expression C<path>. +The returned paths are sufficiently qualified so that they match +exactly one node in the current tree. + +This function returns a NULL-terminated array of strings +(like L<environ(3)>), or NULL if there was an error. +I<The caller must free the strings and the array after use>. + +=head2 guestfs_aug_mv + + int guestfs_aug_mv (guestfs_h *handle, + const char *src, + const char *dest); + +Move the node C<src> to C<dest>. C<src> must match exactly +one node. C<dest> is overwritten if it exists. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_aug_rm + + int guestfs_aug_rm (guestfs_h *handle, + const char *path); + +Remove C<path> and all of its children. + +On success this returns the number of entries which were removed. + +On error this function returns -1. + +=head2 guestfs_aug_save + + int guestfs_aug_save (guestfs_h *handle); + +This writes all pending changes to disk. + +The flags which were passed to C<guestfs_aug_init> affect exactly +how files are saved. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_aug_set + + int guestfs_aug_set (guestfs_h *handle, + const char *path, + const char *val); + +Set the value associated with C<path> to C<value>. + +This function returns 0 on success or -1 on error. + =head2 guestfs_cat char *guestfs_cat (guestfs_h *handle, @@ -193,7 +390,7 @@ I<The caller must free the strings and the array after use>. List all the logical volumes detected. This is the equivalent of the L<lvs(8)> command. The "full" version includes all fields. -This function returns a C<struct guestfs_lvm_lv_list>. +This function returns a C<struct guestfs_lvm_lv_list *>. I<The caller must call C<guestfs_free_lvm_lv_list> after use.>. =head2 guestfs_mount @@ -244,7 +441,7 @@ I<The caller must free the strings and the array after use>. List all the physical volumes detected. This is the equivalent of the L<pvs(8)> command. The "full" version includes all fields. -This function returns a C<struct guestfs_lvm_pv_list>. +This function returns a C<struct guestfs_lvm_pv_list *>. I<The caller must call C<guestfs_free_lvm_pv_list> after use.>. =head2 guestfs_read_lines @@ -352,7 +549,7 @@ I<The caller must free the strings and the array after use>. List all the volumes groups detected. This is the equivalent of the L<vgs(8)> command. The "full" version includes all fields. -This function returns a C<struct guestfs_lvm_vg_list>. +This function returns a C<struct guestfs_lvm_vg_list *>. I<The caller must call C<guestfs_free_lvm_vg_list> after use.>. =head2 guestfs_wait_ready diff --git a/libguestfs.spec.in b/libguestfs.spec.in index bc611eff..4e55988e 100644 --- a/libguestfs.spec.in +++ b/libguestfs.spec.in @@ -15,7 +15,8 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root # Basic build requirements: BuildRequires: /usr/bin/pod2man BuildRequires: /usr/bin/pod2text -BuildRequires: febootstrap +BuildRequires: febootstrap >= 1.5 +BuildRequires: augeas-devel # If you want to build the bindings for different languages: BuildRequires: ocaml diff --git a/make-initramfs.sh.in b/make-initramfs.sh.in index f25e7ce9..4579e901 100755 --- a/make-initramfs.sh.in +++ b/make-initramfs.sh.in @@ -25,7 +25,7 @@ set -e # larger. debug=yes -modules="-i kernel -i bash -i coreutils -i lvm2 -i ntfs-3g -i util-linux-ng -i MAKEDEV -i net-tools" +modules="-i kernel -i bash -i coreutils -i lvm2 -i ntfs-3g -i util-linux-ng -i MAKEDEV -i net-tools -i augeas-libs" if [ "x$debug" = "xyes" ]; then modules="$modules -i module-init-tools -i procps -i strace -i iputils" @@ -38,7 +38,7 @@ rm -f $output rm -f $koutput # Create the basic initramfs. -@FEBOOTSTRAP@ $modules @REPO@ initramfs @MIRROR@ +@FEBOOTSTRAP@ $modules -u @UPDATES@ @REPO@ initramfs @MIRROR@ # /sysroot is where the guest root filesystem will be mounted. mkdir initramfs/sysroot diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index 2504b45e..64878611 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -112,3 +112,15 @@ external pvs_full : t -> lvm_pv array = "ocaml_guestfs_pvs_full" external vgs_full : t -> lvm_vg array = "ocaml_guestfs_vgs_full" external lvs_full : t -> lvm_lv array = "ocaml_guestfs_lvs_full" external read_lines : t -> string -> string array = "ocaml_guestfs_read_lines" +external aug_init : t -> string -> int -> unit = "ocaml_guestfs_aug_init" +external aug_close : t -> unit = "ocaml_guestfs_aug_close" +external aug_defvar : t -> string -> string option -> int = "ocaml_guestfs_aug_defvar" +external aug_defnode : t -> string -> string -> string -> int * bool = "ocaml_guestfs_aug_defnode" +external aug_get : t -> string -> string = "ocaml_guestfs_aug_get" +external aug_set : t -> string -> string -> unit = "ocaml_guestfs_aug_set" +external aug_insert : t -> string -> string -> bool -> unit = "ocaml_guestfs_aug_insert" +external aug_rm : t -> string -> int = "ocaml_guestfs_aug_rm" +external aug_mv : t -> string -> string -> unit = "ocaml_guestfs_aug_mv" +external aug_match : t -> string -> string array = "ocaml_guestfs_aug_match" +external aug_save : t -> unit = "ocaml_guestfs_aug_save" +external aug_load : t -> unit = "ocaml_guestfs_aug_load" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 177f09e8..b1452abe 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -175,3 +175,39 @@ val lvs_full : t -> lvm_lv array val read_lines : t -> string -> string array (** read file as lines *) +val aug_init : t -> string -> int -> unit +(** create a new Augeas handle *) + +val aug_close : t -> unit +(** close the current Augeas handle *) + +val aug_defvar : t -> string -> string option -> int +(** define an Augeas variable *) + +val aug_defnode : t -> string -> string -> string -> int * bool +(** define an Augeas node *) + +val aug_get : t -> string -> string +(** look up the value of an Augeas path *) + +val aug_set : t -> string -> string -> unit +(** set Augeas path to value *) + +val aug_insert : t -> string -> string -> bool -> unit +(** insert a sibling Augeas node *) + +val aug_rm : t -> string -> int +(** remove an Augeas path *) + +val aug_mv : t -> string -> string -> unit +(** move Augeas node *) + +val aug_match : t -> string -> string array +(** return Augeas nodes which match path *) + +val aug_save : t -> unit +(** write all pending Augeas changes to disk *) + +val aug_load : t -> unit +(** load files into the tree *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index 80a891e8..f21d63ec 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -456,7 +456,7 @@ ocaml_guestfs_get_autosync (value gv) if (r == -1) ocaml_guestfs_raise_error (g, "get_autosync"); - rv = r ? Val_true : Val_false; + rv = Val_bool (r); CAMLreturn (rv); } @@ -501,7 +501,7 @@ ocaml_guestfs_get_verbose (value gv) if (r == -1) ocaml_guestfs_raise_error (g, "get_verbose"); - rv = r ? Val_true : Val_false; + rv = Val_bool (r); CAMLreturn (rv); } @@ -868,3 +868,292 @@ ocaml_guestfs_read_lines (value gv, value pathv) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_aug_init (value gv, value rootv, value flagsv) +{ + CAMLparam3 (gv, rootv, flagsv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_init: used handle after closing it"); + + const char *root = String_val (rootv); + int flags = Int_val (flagsv); + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_init (g, root, flags); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_init"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_close (value gv) +{ + CAMLparam1 (gv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_close: used handle after closing it"); + + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_close (g); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_close"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_defvar (value gv, value namev, value exprv) +{ + CAMLparam3 (gv, namev, exprv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_defvar: used handle after closing it"); + + const char *name = String_val (namev); + const char *expr = + exprv != Val_int (0) ? String_val (Field (exprv, 0)) : NULL; + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_defvar (g, name, expr); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_defvar"); + + rv = Val_int (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_defnode (value gv, value namev, value exprv, value valv) +{ + CAMLparam4 (gv, namev, exprv, valv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_defnode: used handle after closing it"); + + const char *name = String_val (namev); + const char *expr = String_val (exprv); + const char *val = String_val (valv); + struct guestfs_int_bool *r; + + caml_enter_blocking_section (); + r = guestfs_aug_defnode (g, name, expr, val); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "aug_defnode"); + + rv = caml_alloc (2, 0); + Store_field (rv, 0, Val_int (r->i)); + Store_field (rv, 1, Val_bool (r->b)); + guestfs_free_int_bool (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_get (value gv, value pathv) +{ + CAMLparam2 (gv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_get: used handle after closing it"); + + const char *path = String_val (pathv); + char *r; + + caml_enter_blocking_section (); + r = guestfs_aug_get (g, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "aug_get"); + + rv = caml_copy_string (r); + free (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_set (value gv, value pathv, value valv) +{ + CAMLparam3 (gv, pathv, valv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_set: used handle after closing it"); + + const char *path = String_val (pathv); + const char *val = String_val (valv); + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_set (g, path, val); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_set"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_insert (value gv, value pathv, value labelv, value beforev) +{ + CAMLparam4 (gv, pathv, labelv, beforev); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_insert: used handle after closing it"); + + const char *path = String_val (pathv); + const char *label = String_val (labelv); + int before = Bool_val (beforev); + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_insert (g, path, label, before); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_insert"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_rm (value gv, value pathv) +{ + CAMLparam2 (gv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_rm: used handle after closing it"); + + const char *path = String_val (pathv); + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_rm (g, path); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_rm"); + + rv = Val_int (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_mv (value gv, value srcv, value destv) +{ + CAMLparam3 (gv, srcv, destv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_mv: used handle after closing it"); + + const char *src = String_val (srcv); + const char *dest = String_val (destv); + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_mv (g, src, dest); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_mv"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_match (value gv, value pathv) +{ + CAMLparam2 (gv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_match: used handle after closing it"); + + const char *path = String_val (pathv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_aug_match (g, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "aug_match"); + + rv = caml_copy_string_array ((const char **) r); + for (i = 0; r[i] != NULL; ++i) free (r[i]); + free (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_save (value gv) +{ + CAMLparam1 (gv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_save: used handle after closing it"); + + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_save (g); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_save"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_aug_load (value gv) +{ + CAMLparam1 (gv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("aug_load: used handle after closing it"); + + int r; + + caml_enter_blocking_section (); + r = guestfs_aug_load (g); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "aug_load"); + + rv = Val_unit; + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index 3cc47750..99eb3190 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -485,3 +485,141 @@ PREINIT: } free (lines); +void +aug_init (g, root, flags) + guestfs_h *g; + char *root; + int flags; + PPCODE: + if (guestfs_aug_init (g, root, flags) == -1) + croak ("aug_init: %s", last_error); + +void +aug_close (g) + guestfs_h *g; + PPCODE: + if (guestfs_aug_close (g) == -1) + croak ("aug_close: %s", last_error); + +SV * +aug_defvar (g, name, expr) + guestfs_h *g; + char *name; + char *expr; +PREINIT: + int nrnodes; + CODE: + nrnodes = guestfs_aug_defvar (g, name, expr); + if (nrnodes == -1) + croak ("aug_defvar: %s", last_error); + RETVAL = newSViv (nrnodes); + OUTPUT: + RETVAL + +void +aug_defnode (g, name, expr, val) + guestfs_h *g; + char *name; + char *expr; + char *val; +PREINIT: + struct guestfs_int_bool *r; + PPCODE: + r = guestfs_aug_defnode (g, name, expr, val); + if (r == NULL) + croak ("aug_defnode: %s", last_error); + EXTEND (SP, 2); + PUSHs (sv_2mortal (newSViv (r->i))); + PUSHs (sv_2mortal (newSViv (r->b))); + guestfs_free_int_bool (r); + +SV * +aug_get (g, path) + guestfs_h *g; + char *path; +PREINIT: + char *val; + CODE: + val = guestfs_aug_get (g, path); + if (val == NULL) + croak ("aug_get: %s", last_error); + RETVAL = newSVpv (val, 0); + free (val); + OUTPUT: + RETVAL + +void +aug_set (g, path, val) + guestfs_h *g; + char *path; + char *val; + PPCODE: + if (guestfs_aug_set (g, path, val) == -1) + croak ("aug_set: %s", last_error); + +void +aug_insert (g, path, label, before) + guestfs_h *g; + char *path; + char *label; + int before; + PPCODE: + if (guestfs_aug_insert (g, path, label, before) == -1) + croak ("aug_insert: %s", last_error); + +SV * +aug_rm (g, path) + guestfs_h *g; + char *path; +PREINIT: + int nrnodes; + CODE: + nrnodes = guestfs_aug_rm (g, path); + if (nrnodes == -1) + croak ("aug_rm: %s", last_error); + RETVAL = newSViv (nrnodes); + OUTPUT: + RETVAL + +void +aug_mv (g, src, dest) + guestfs_h *g; + char *src; + char *dest; + PPCODE: + if (guestfs_aug_mv (g, src, dest) == -1) + croak ("aug_mv: %s", last_error); + +void +aug_match (g, path) + guestfs_h *g; + char *path; +PREINIT: + char **matches; + int i, n; + PPCODE: + matches = guestfs_aug_match (g, path); + if (matches == NULL) + croak ("aug_match: %s", last_error); + for (n = 0; matches[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (matches[i], 0))); + free (matches[i]); + } + free (matches); + +void +aug_save (g) + guestfs_h *g; + PPCODE: + if (guestfs_aug_save (g) == -1) + croak ("aug_save: %s", last_error); + +void +aug_load (g) + guestfs_h *g; + PPCODE: + if (guestfs_aug_load (g) == -1) + croak ("aug_load: %s", last_error); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index 7706ae40..29414662 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -112,6 +112,134 @@ image). This is equivalent to the qemu parameter C<-drive file=filename>. +=item $h->aug_close (); + +Close the current Augeas handle and free up any resources +used by it. After calling this, you have to call +C<$h-E<gt>aug_init> again before you can use any other +Augeas functions. + +=item ($nrnodes, $created) = $h->aug_defnode (name, expr, val); + +Defines a variable C<name> whose value is the result of +evaluating C<expr>. + +If C<expr> evaluates to an empty nodeset, a node is created, +equivalent to calling C<$h-E<gt>aug_set> C<expr>, C<value>. +C<name> will be the nodeset containing that single node. + +On success this returns a pair containing the +number of nodes in the nodeset, and a boolean flag +if a node was created. + +=item $nrnodes = $h->aug_defvar (name, expr); + +Defines an Augeas variable C<name> whose value is the result +of evaluating C<expr>. If C<expr> is NULL, then C<name> is +undefined. + +On success this returns the number of nodes in C<expr>, or +C<0> if C<expr> evaluates to something which is not a nodeset. + +=item $val = $h->aug_get (path); + +Look up the value associated with C<path>. If C<path> +matches exactly one node, the C<value> is returned. + +=item $h->aug_init (root, flags); + +Create a new Augeas handle for editing configuration files. +If there was any previous Augeas handle associated with this +guestfs session, then it is closed. + +You must call this before using any other C<$h-E<gt>aug_*> +commands. + +C<root> is the filesystem root. C<root> must not be NULL, +use C</> instead. + +The flags are the same as the flags defined in +E<lt>augeas.hE<gt>, the logical I<or> of the following +integers: + +=over 4 + +=item 1 C<AUG_SAVE_BACKUP> + +Keep the original file with a C<.augsave> extension. + +=item 2 C<AUG_SAVE_NEWFILE> + +Save changes into a file with extension C<.augnew>, and +do not overwrite original. Overrides C<AUG_SAVE_BACKUP>. + +=item 4 C<AUG_TYPE_CHECK> + +Typecheck lenses (can be expensive). + +=item 8 C<AUG_NO_STDINC> + +Do not use standard load path for modules. + +=item 16 C<AUG_SAVE_NOOP> + +Make save a no-op, just record what would have been changed. + +=item 32 C<AUG_NO_LOAD> + +Do not load the tree in C<$h-E<gt>aug_init>. + +=back + +To close the handle, you can call C<$h-E<gt>aug_close>. + +To find out more about Augeas, see L<http://augeas.net/>. + +=item $h->aug_insert (path, label, before); + +Create a new sibling C<label> for C<path>, inserting it into +the tree before or after C<path> (depending on the boolean +flag C<before>). + +C<path> must match exactly one existing node in the tree, and +C<label> must be a label, ie. not contain C</>, C<*> or end +with a bracketed index C<[N]>. + +=item $h->aug_load (); + +Load files into the tree. + +See C<aug_load> in the Augeas documentation for the full gory +details. + +=item @matches = $h->aug_match (path); + +Returns a list of paths which match the path expression C<path>. +The returned paths are sufficiently qualified so that they match +exactly one node in the current tree. + +=item $h->aug_mv (src, dest); + +Move the node C<src> to C<dest>. C<src> must match exactly +one node. C<dest> is overwritten if it exists. + +=item $nrnodes = $h->aug_rm (path); + +Remove C<path> and all of its children. + +On success this returns the number of entries which were removed. + +=item $h->aug_save (); + +This writes all pending changes to disk. + +The flags which were passed to C<$h-E<gt>aug_init> affect exactly +how files are saved. + +=item $h->aug_set (path, val); + +Set the value associated with C<path> to C<value>. + =item $content = $h->cat (path); Return the contents of the file named C<path>. diff --git a/src/generator.ml b/src/generator.ml index af19fdad..3f42c391 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -32,17 +32,25 @@ and ret = * indication, ie. 0 or -1. *) | Err + (* "Int" as a return value means an int which is -1 for error + * or any value >= 0 on success. + *) + | RInt of string (* "RBool" is a bool return value which can be true/false or * -1 for error. *) | RBool of string (* "RConstString" is a string that refers to a constant value. - * Try to avoid using this. + * Try to avoid using this. In particular you cannot use this + * for values returned from the daemon, because there is no + * thread-safe way to return them in the C API. *) | RConstString of string (* "RString" and "RStringList" are caller-frees. *) | RString of string | RStringList of string + (* Some limited tuples are possible: *) + | RIntBool of string * string (* LVM PVs, VGs and LVs. *) | RPVList of string | RVGList of string @@ -52,10 +60,12 @@ and args = | P0 | P1 of argt | P2 of argt * argt + | P3 of argt * argt * argt and argt = | String of string (* const char *name, cannot be NULL *) | OptString of string (* const char *name, may be NULL *) | Bool of string (* boolean *) + | Int of string (* int (smallish ints, signed, <= 31 bits) *) type flags = | ProtocolLimitWarning (* display warning about protocol size limits *) @@ -323,6 +333,146 @@ Note that this function cannot correctly handle binary files (specifically, files containing C<\\0> character which is treated as end of line). For those you need to use the C<guestfs_read_file> function which has a more complex interface."); + + ("aug_init", (Err, P2 (String "root", Int "flags")), 16, [], + "create a new Augeas handle", + "\ +Create a new Augeas handle for editing configuration files. +If there was any previous Augeas handle associated with this +guestfs session, then it is closed. + +You must call this before using any other C<guestfs_aug_*> +commands. + +C<root> is the filesystem root. C<root> must not be NULL, +use C</> instead. + +The flags are the same as the flags defined in +E<lt>augeas.hE<gt>, the logical I<or> of the following +integers: + +=over 4 + +=item 1 C<AUG_SAVE_BACKUP> + +Keep the original file with a C<.augsave> extension. + +=item 2 C<AUG_SAVE_NEWFILE> + +Save changes into a file with extension C<.augnew>, and +do not overwrite original. Overrides C<AUG_SAVE_BACKUP>. + +=item 4 C<AUG_TYPE_CHECK> + +Typecheck lenses (can be expensive). + +=item 8 C<AUG_NO_STDINC> + +Do not use standard load path for modules. + +=item 16 C<AUG_SAVE_NOOP> + +Make save a no-op, just record what would have been changed. + +=item 32 C<AUG_NO_LOAD> + +Do not load the tree in C<guestfs_aug_init>. + +=back + +To close the handle, you can call C<guestfs_aug_close>. + +To find out more about Augeas, see L<http://augeas.net/>."); + + ("aug_close", (Err, P0), 26, [], + "close the current Augeas handle", + "\ +Close the current Augeas handle and free up any resources +used by it. After calling this, you have to call +C<guestfs_aug_init> again before you can use any other +Augeas functions."); + + ("aug_defvar", (RInt "nrnodes", P2 (String "name", OptString "expr")), 17, [], + "define an Augeas variable", + "\ +Defines an Augeas variable C<name> whose value is the result +of evaluating C<expr>. If C<expr> is NULL, then C<name> is +undefined. + +On success this returns the number of nodes in C<expr>, or +C<0> if C<expr> evaluates to something which is not a nodeset."); + + ("aug_defnode", (RIntBool ("nrnodes", "created"), P3 (String "name", String "expr", String "val")), 18, [], + "define an Augeas node", + "\ +Defines a variable C<name> whose value is the result of +evaluating C<expr>. + +If C<expr> evaluates to an empty nodeset, a node is created, +equivalent to calling C<guestfs_aug_set> C<expr>, C<value>. +C<name> will be the nodeset containing that single node. + +On success this returns a pair containing the +number of nodes in the nodeset, and a boolean flag +if a node was created."); + + ("aug_get", (RString "val", P1 (String "path")), 19, [], + "look up the value of an Augeas path", + "\ +Look up the value associated with C<path>. If C<path> +matches exactly one node, the C<value> is returned."); + + ("aug_set", (Err, P2 (String "path", String "val")), 20, [], + "set Augeas path to value", + "\ +Set the value associated with C<path> to C<value>."); + + ("aug_insert", (Err, P3 (String "path", String "label", Bool "before")), 21, [], + "insert a sibling Augeas node", + "\ +Create a new sibling C<label> for C<path>, inserting it into +the tree before or after C<path> (depending on the boolean +flag C<before>). + +C<path> must match exactly one existing node in the tree, and +C<label> must be a label, ie. not contain C</>, C<*> or end +with a bracketed index C<[N]>."); + + ("aug_rm", (RInt "nrnodes", P1 (String "path")), 22, [], + "remove an Augeas path", + "\ +Remove C<path> and all of its children. + +On success this returns the number of entries which were removed."); + + ("aug_mv", (Err, P2 (String "src", String "dest")), 23, [], + "move Augeas node", + "\ +Move the node C<src> to C<dest>. C<src> must match exactly +one node. C<dest> is overwritten if it exists."); + + ("aug_match", (RStringList "matches", P1 (String "path")), 24, [], + "return Augeas nodes which match path", + "\ +Returns a list of paths which match the path expression C<path>. +The returned paths are sufficiently qualified so that they match +exactly one node in the current tree."); + + ("aug_save", (Err, P0), 25, [], + "write all pending Augeas changes to disk", + "\ +This writes all pending changes to disk. + +The flags which were passed to C<guestfs_aug_init> affect exactly +how files are saved."); + + ("aug_load", (Err, P0), 27, [], + "load files into the tree", + "\ +Load files into the tree. + +See C<aug_load> in the Augeas documentation for the full gory +details."); ] let all_functions = non_daemon_functions @ daemon_functions @@ -465,18 +615,25 @@ let iter_args f = function | P0 -> () | P1 arg1 -> f arg1 | P2 (arg1, arg2) -> f arg1; f arg2 + | P3 (arg1, arg2, arg3) -> f arg1; f arg2; f arg3 let iteri_args f = function | P0 -> () | P1 arg1 -> f 0 arg1 | P2 (arg1, arg2) -> f 0 arg1; f 1 arg2 + | P3 (arg1, arg2, arg3) -> f 0 arg1; f 1 arg2; f 2 arg3 let map_args f = function | P0 -> [] | P1 arg1 -> [f arg1] - | P2 (arg1, arg2) -> [f arg1; f arg2] + | P2 (arg1, arg2) -> + let n1 = f arg1 in let n2 = f arg2 in [n1; n2] + | P3 (arg1, arg2, arg3) -> + let n1 = f arg1 in let n2 = f arg2 in let n3 = f arg3 in [n1; n2; n3] + +let nr_args = function | P0 -> 0 | P1 _ -> 1 | P2 _ -> 2 | P3 _ -> 3 -let nr_args = function | P0 -> 0 | P1 _ -> 1 | P2 _ -> 2 +let name_of_argt = function String n | OptString n | Bool n | Int n -> n (* Check function names etc. for consistency. *) let check_functions () = @@ -583,6 +740,8 @@ and generate_actions_pod () = (match fst style with | Err -> pr "This function returns 0 on success or -1 on error.\n\n" + | RInt _ -> + pr "On error this function returns -1.\n\n" | RBool _ -> pr "This function returns a C truth value on success or -1 on error.\n\n" | RConstString _ -> @@ -595,14 +754,17 @@ I<The caller must free the returned string after use>.\n\n" pr "This function returns a NULL-terminated array of strings (like L<environ(3)>), or NULL if there was an error. I<The caller must free the strings and the array after use>.\n\n" + | RIntBool _ -> + pr "This function returns a C<struct guestfs_int_bool *>. +I<The caller must call C<guestfs_free_int_bool> after use.>.\n\n" | RPVList _ -> - pr "This function returns a C<struct guestfs_lvm_pv_list>. + pr "This function returns a C<struct guestfs_lvm_pv_list *>. I<The caller must call C<guestfs_free_lvm_pv_list> after use.>.\n\n" | RVGList _ -> - pr "This function returns a C<struct guestfs_lvm_vg_list>. + pr "This function returns a C<struct guestfs_lvm_vg_list *>. I<The caller must call C<guestfs_free_lvm_vg_list> after use.>.\n\n" | RLVList _ -> - pr "This function returns a C<struct guestfs_lvm_lv_list>. + pr "This function returns a C<struct guestfs_lvm_lv_list *>. I<The caller must call C<guestfs_free_lvm_lv_list> after use.>.\n\n" ); if List.mem ProtocolLimitWarning flags then @@ -676,21 +838,26 @@ and generate_xdr () = List.iter ( fun(shortname, style, _, _, _, _) -> let name = "guestfs_" ^ shortname in - pr "/* %s */\n\n" name; + (match snd style with | P0 -> () | args -> pr "struct %s_args {\n" name; iter_args ( function - | String name -> pr " string %s<>;\n" name - | OptString name -> pr " string *%s<>;\n" name - | Bool name -> pr " bool %s;\n" name + | String n -> pr " string %s<>;\n" n + | OptString n -> pr " str *%s;\n" n + | Bool n -> pr " bool %s;\n" n + | Int n -> pr " int %s;\n" n ) args; pr "};\n\n" ); (match fst style with | Err -> () + | RInt n -> + pr "struct %s_ret {\n" name; + pr " int %s;\n" n; + pr "};\n\n" | RBool n -> pr "struct %s_ret {\n" name; pr " bool %s;\n" n; @@ -705,6 +872,11 @@ and generate_xdr () = pr "struct %s_ret {\n" name; pr " str %s<>;\n" n; pr "};\n\n" + | RIntBool (n,m) -> + pr "struct %s_ret {\n" name; + pr " int %s;\n" n; + pr " bool %s;\n" m; + pr "};\n\n" | RPVList n -> pr "struct %s_ret {\n" name; pr " guestfs_lvm_int_pv_list %s;\n" n; @@ -788,6 +960,13 @@ and generate_structs_h () = * must be identical to what rpcgen / the RFC defines. *) + (* guestfs_int_bool structure. *) + pr "struct guestfs_int_bool {\n"; + pr " int32_t i;\n"; + pr " int32_t b;\n"; + pr "};\n"; + pr "\n"; + (* LVM public structures. *) List.iter ( function @@ -838,7 +1017,9 @@ and generate_client_actions () = | Err -> () | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" + | RInt _ | RBool _ | RString _ | RStringList _ + | RIntBool _ | RPVList _ | RVGList _ | RLVList _ -> pr " struct %s_ret ret;\n" name ); @@ -865,7 +1046,9 @@ and generate_client_actions () = | Err -> () | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" + | RInt _ | RBool _ | RString _ | RStringList _ + | RIntBool _ | RPVList _ | RVGList _ | RLVList _ -> pr " if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name; pr " error (g, \"%s: failed to parse reply\");\n" name; @@ -884,10 +1067,11 @@ and generate_client_actions () = let error_code = match fst style with - | Err | RBool _ -> "-1" + | Err | RInt _ | RBool _ -> "-1" | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" - | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ -> + | RString _ | RStringList _ | RIntBool _ + | RPVList _ | RVGList _ | RLVList _ -> "NULL" in pr "{\n"; @@ -917,12 +1101,14 @@ and generate_client_actions () = | args -> iter_args ( function - | String name -> - pr " args.%s = (char *) %s;\n" name name - | OptString name -> - pr " args.%s = %s ? *%s : NULL;\n" name name name - | Bool name -> - pr " args.%s = %s;\n" name name + | String n -> + pr " args.%s = (char *) %s;\n" n n + | OptString n -> + pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n + | Bool n -> + pr " args.%s = %s;\n" n n + | Int n -> + pr " args.%s = %s;\n" n n ) args; pr " serial = dispatch (g, GUESTFS_PROC_%s,\n" (String.uppercase shortname); @@ -958,6 +1144,7 @@ and generate_client_actions () = (match fst style with | Err -> pr " return 0;\n" + | RInt n | RBool n -> pr " return rv.ret.%s;\n" n | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" @@ -971,6 +1158,9 @@ and generate_client_actions () = n n; pr " rv.ret.%s.%s_val[rv.ret.%s.%s_len] = NULL;\n" n n n n; pr " return rv.ret.%s.%s_val;\n" n n + | 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 @@ -995,7 +1185,8 @@ and generate_daemon_actions_h () = List.iter ( fun (name, style, _, _, _, _) -> generate_prototype - ~single_line:true ~newline:true ~in_daemon:true ("do_" ^ name) style; + ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_" + name style; ) daemon_functions (* Generate the server-side stubs. *) @@ -1024,12 +1215,13 @@ and generate_daemon_actions () = pr "{\n"; let error_code = match fst style with - | Err -> pr " int r;\n"; "-1" + | Err | RInt _ -> pr " int r;\n"; "-1" | RBool _ -> pr " int r;\n"; "-1" | RConstString _ -> failwithf "RConstString cannot be returned from a daemon function" | RString _ -> pr " char *r;\n"; "NULL" | RStringList _ -> 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" | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL" in @@ -1040,9 +1232,10 @@ and generate_daemon_actions () = pr " struct guestfs_%s_args args;\n" name; iter_args ( function - | String name - | OptString name -> pr " const char *%s;\n" name - | Bool name -> pr " int %s;\n" name + | String n + | OptString n -> pr " const char *%s;\n" n + | Bool n -> pr " int %s;\n" n + | Int n -> pr " int %s;\n" n ) args ); pr "\n"; @@ -1053,14 +1246,15 @@ and generate_daemon_actions () = pr " memset (&args, 0, sizeof args);\n"; pr "\n"; pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name; - pr " reply_with_error (\"%s: daemon failed to decode procedure arguments\");\n" name; + pr " reply_with_error (\"%%s: daemon failed to decode procedure arguments\", \"%s\");\n" name; pr " return;\n"; pr " }\n"; iter_args ( function - | String name -> pr " %s = args.%s;\n" name name - | OptString name -> pr " %s = args.%s;\n" name name (* XXX? *) - | Bool name -> pr " %s = args.%s;\n" name name + | String n -> pr " %s = args.%s;\n" n n + | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n + | Bool n -> pr " %s = args.%s;\n" n n + | Int n -> pr " %s = args.%s;\n" n n ) args; pr "\n" ); @@ -1076,6 +1270,10 @@ and generate_daemon_actions () = (match fst style with | Err -> pr " reply (NULL, NULL);\n" + | RInt 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 | RBool n -> pr " struct guestfs_%s_ret ret;\n" name; pr " ret.%s = r;\n" n; @@ -1093,20 +1291,23 @@ and generate_daemon_actions () = pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name; pr " free_strings (r);\n" + | 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 " 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 " 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 -> 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 " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name; pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name ); @@ -1348,12 +1549,7 @@ and generate_fish_cmds () = | P0 -> name2 | args -> sprintf "%s <%s>" - name2 ( - String.concat "> <" ( - map_args (function - | String n | OptString n | Bool n -> n) args - ) - ) in + name2 (String.concat "> <" (map_args name_of_argt args)) in let warnings = if List.mem ProtocolLimitWarning flags then @@ -1429,19 +1625,22 @@ FTP." pr "{\n"; (match fst style with | Err + | RInt _ | RBool _ -> pr " int r;\n" | RConstString _ -> pr " const char *r;\n" | RString _ -> pr " char *r;\n" | RStringList _ -> 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" | RLVList _ -> pr " struct guestfs_lvm_lv_list *r;\n" ); iter_args ( function - | String name -> pr " const char *%s;\n" name - | OptString name -> pr " const char *%s;\n" name - | Bool name -> pr " int %s;\n" name + | String n -> pr " const char *%s;\n" n + | OptString n -> pr " const char *%s;\n" n + | Bool n -> pr " int %s;\n" n + | Int n -> pr " int %s;\n" n ) (snd style); (* Check and convert parameters. *) @@ -1461,6 +1660,8 @@ FTP." name i i | Bool name -> pr " %s = is_true (argv[%d]) ? 1 : 0;\n" name i + | Int name -> + pr " %s = atoi (argv[%d]);\n" name i ) (snd style); (* Call C API function. *) @@ -1474,6 +1675,10 @@ FTP." (* Check return value for errors and display command results. *) (match fst style with | Err -> pr " return r;\n" + | RInt _ -> + pr " if (r == -1) return -1;\n"; + pr " if (r) printf (\"%%d\\n\", r);\n"; + pr " return 0;\n" | RBool _ -> pr " if (r == -1) return -1;\n"; pr " if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n"; @@ -1492,6 +1697,12 @@ FTP." pr " print_strings (r);\n"; pr " free_strings (r);\n"; pr " return 0;\n" + | RIntBool _ -> + pr " if (r == NULL) return -1;\n"; + pr " printf (\"%%d, %%s\\n\", r->i,\n"; + pr " r->b ? \"true\" : \"false\");\n"; + pr " guestfs_free_int_bool (r);\n"; + pr " return 0;\n" | RPVList _ -> pr " if (r == NULL) return -1;\n"; pr " print_pv_list (r);\n"; @@ -1565,6 +1776,7 @@ and generate_fish_actions_pod () = | String n -> pr " %s" n | OptString n -> pr " %s" n | Bool _ -> pr " true|false" + | Int n -> pr " %s" n ) (snd style); pr "\n"; pr "\n"; @@ -1574,15 +1786,20 @@ and generate_fish_actions_pod () = (* Generate a C function prototype. *) and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) ?(single_line = false) ?(newline = false) ?(in_daemon = false) + ?(prefix = "") ?handle name style = if extern then pr "extern "; if static then pr "static "; (match fst style with | Err -> pr "int " + | RInt _ -> pr "int " | RBool _ -> pr "int " | RConstString _ -> pr "const char *" | RString _ -> pr "char *" | RStringList _ -> pr "char **" + | RIntBool _ -> + if not in_daemon then pr "struct guestfs_int_bool *" + else pr "guestfs_%s_ret *" name | RPVList _ -> if not in_daemon then pr "struct guestfs_lvm_pv_list *" else pr "guestfs_lvm_int_pv_list *" @@ -1593,24 +1810,29 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) if not in_daemon then pr "struct guestfs_lvm_lv_list *" else pr "guestfs_lvm_int_lv_list *" ); - pr "%s (" name; - let comma = ref false in - (match handle with - | None -> () - | Some handle -> pr "guestfs_h *%s" handle; comma := true - ); - let next () = - if !comma then ( - if single_line then pr ", " else pr ",\n\t\t" + pr "%s%s (" prefix name; + if handle = None && nr_args (snd style) = 0 then + pr "void" + else ( + let comma = ref false in + (match handle with + | None -> () + | Some handle -> pr "guestfs_h *%s" handle; comma := true ); - comma := true - in - iter_args ( - function - | String name -> next (); pr "const char *%s" name - | OptString name -> next (); pr "const char *%s" name - | Bool name -> next (); pr "int %s" name - ) (snd style); + let next () = + if !comma then ( + if single_line then pr ", " else pr ",\n\t\t" + ); + comma := true + in + iter_args ( + function + | String n -> next (); pr "const char *%s" n + | OptString n -> next (); pr "const char *%s" n + | Bool n -> next (); pr "int %s" n + | Int n -> next (); pr "int %s" n + ) (snd style); + ); pr ")"; if semicolon then pr ";"; if newline then pr "\n" @@ -1628,9 +1850,10 @@ and generate_call_args ?handle style = if !comma then pr ", "; comma := true; match arg with - | String name -> pr "%s" name - | OptString name -> pr "%s" name - | Bool name -> pr "%s" name + | String n -> pr "%s" n + | OptString n -> pr "%s" n + | Bool n -> pr "%s" n + | Int n -> pr "%s" n ) (snd style); pr ")" @@ -1779,15 +2002,13 @@ and generate_ocaml_c () = pr "CAMLprim value\n"; pr "ocaml_guestfs_%s (value gv" name; iter_args ( - function - | String n | OptString n | Bool n -> pr ", value %sv" n + fun arg -> pr ", value %sv" (name_of_argt arg) ) (snd style); pr ")\n"; pr "{\n"; pr " CAMLparam%d (gv" (1 + (nr_args (snd style))); iter_args ( - function - | String n | OptString n | Bool n -> pr ", %sv" n + fun arg -> pr ", %sv" (name_of_argt arg) ) (snd style); pr ");\n"; pr " CAMLlocal1 (rv);\n"; @@ -1808,10 +2029,13 @@ and generate_ocaml_c () = n n | Bool n -> pr " int %s = Bool_val (%sv);\n" n n + | Int n -> + pr " int %s = Int_val (%sv);\n" n n ) (snd style); let error_code = match fst style with | Err -> pr " int r;\n"; "-1" + | RInt _ -> pr " int r;\n"; "-1" | RBool _ -> pr " int r;\n"; "-1" | RConstString _ -> pr " const char *r;\n"; "NULL" | RString _ -> pr " char *r;\n"; "NULL" @@ -1819,6 +2043,9 @@ and generate_ocaml_c () = pr " int i;\n"; pr " char **r;\n"; "NULL" + | RIntBool _ -> + pr " struct guestfs_int_bool *r;\n"; + "NULL" | RPVList _ -> pr " struct guestfs_lvm_pv_list *r;\n"; "NULL" @@ -1841,7 +2068,8 @@ and generate_ocaml_c () = (match fst style with | Err -> pr " rv = Val_unit;\n" - | RBool _ -> pr " rv = r ? Val_true : Val_false;\n" + | RInt _ -> pr " rv = Val_int (r);\n" + | RBool _ -> pr " rv = Val_bool (r);\n" | RConstString _ -> pr " rv = caml_copy_string (r);\n" | RString _ -> pr " rv = caml_copy_string (r);\n"; @@ -1850,6 +2078,11 @@ and generate_ocaml_c () = pr " rv = caml_copy_string_array ((const char **) r);\n"; pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n"; pr " free (r);\n" + | RIntBool _ -> + pr " rv = caml_alloc (2, 0);\n"; + pr " Store_field (rv, 0, Val_int (r->i));\n"; + pr " Store_field (rv, 1, Val_bool (r->b));\n"; + pr " guestfs_free_int_bool (r);\n"; | RPVList _ -> pr " rv = copy_lvm_pv_list (r);\n"; pr " guestfs_free_lvm_pv_list (r);\n"; @@ -1890,13 +2123,16 @@ and generate_ocaml_prototype ?(is_external = false) name style = | String _ -> pr "string -> " | OptString _ -> pr "string option -> " | Bool _ -> pr "bool -> " + | Int _ -> pr "int -> " ) (snd style); (match fst style with | Err -> pr "unit" (* all errors are turned into exceptions *) + | RInt _ -> pr "int" | RBool _ -> pr "bool" | RConstString _ -> pr "string" | RString _ -> pr "string" | RStringList _ -> pr "string array" + | RIntBool _ -> pr "int * bool" | RPVList _ -> pr "lvm_pv array" | RVGList _ -> pr "lvm_vg array" | RLVList _ -> pr "lvm_lv array" @@ -1987,10 +2223,12 @@ DESTROY (g) fun (name, style, _, _, _, _) -> (match fst style with | Err -> pr "void\n" + | RInt _ -> pr "SV *\n" | RBool _ -> pr "SV *\n" | RConstString _ -> pr "SV *\n" | RString _ -> pr "SV *\n" | RStringList _ + | RIntBool _ | RPVList _ | RVGList _ | RLVList _ -> pr "void\n" (* all lists returned implictly on the stack *) ); @@ -2004,6 +2242,7 @@ DESTROY (g) | String n -> pr " char *%s;\n" n | OptString n -> pr " char *%s;\n" n | Bool n -> pr " int %s;\n" n + | Int n -> pr " int %s;\n" n ) (snd style); (* Code. *) (match fst style with @@ -2013,21 +2252,22 @@ DESTROY (g) generate_call_args ~handle:"g" style; pr " == -1)\n"; pr " croak (\"%s: %%s\", last_error);\n" name - | RConstString n -> + | RInt n + | RBool n -> pr "PREINIT:\n"; - pr " const char *%s;\n" n; + pr " int %s;\n" n; pr " CODE:\n"; pr " %s = guestfs_%s " n name; generate_call_args ~handle:"g" style; pr ";\n"; - pr " if (%s == NULL)\n" n; + pr " if (%s == -1)\n" n; pr " croak (\"%s: %%s\", last_error);\n" name; - pr " RETVAL = newSVpv (%s, 0);\n" n; + pr " RETVAL = newSViv (%s);\n" n; pr " OUTPUT:\n"; pr " RETVAL\n" - | RString n -> + | RConstString n -> pr "PREINIT:\n"; - pr " char *%s;\n" n; + pr " const char *%s;\n" n; pr " CODE:\n"; pr " %s = guestfs_%s " n name; generate_call_args ~handle:"g" style; @@ -2035,19 +2275,19 @@ DESTROY (g) pr " if (%s == NULL)\n" n; pr " croak (\"%s: %%s\", last_error);\n" name; pr " RETVAL = newSVpv (%s, 0);\n" n; - pr " free (%s);\n" n; pr " OUTPUT:\n"; pr " RETVAL\n" - | RBool n -> + | RString n -> pr "PREINIT:\n"; - pr " int %s;\n" n; + pr " char *%s;\n" n; pr " CODE:\n"; pr " %s = guestfs_%s " n name; generate_call_args ~handle:"g" style; pr ";\n"; - pr " if (%s == -1)\n" n; + pr " if (%s == NULL)\n" n; pr " croak (\"%s: %%s\", last_error);\n" name; - pr " RETVAL = newSViv (%s);\n" n; + pr " RETVAL = newSVpv (%s, 0);\n" n; + pr " free (%s);\n" n; pr " OUTPUT:\n"; pr " RETVAL\n" | RStringList n -> @@ -2067,6 +2307,19 @@ DESTROY (g) pr " free (%s[i]);\n" n; pr " }\n"; pr " free (%s);\n" n; + | RIntBool _ -> + pr "PREINIT:\n"; + pr " struct guestfs_int_bool *r;\n"; + pr " PPCODE:\n"; + pr " r = guestfs_%s " name; + generate_call_args ~handle:"g" style; + pr ";\n"; + pr " if (r == NULL)\n"; + pr " croak (\"%s: %%s\", last_error);\n" name; + pr " EXTEND (SP, 2);\n"; + pr " PUSHs (sv_2mortal (newSViv (r->i)));\n"; + 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; | RVGList n -> @@ -2236,8 +2489,10 @@ and generate_perl_prototype name style = (match fst style with | Err -> () | RBool n + | RInt n | RConstString n | RString n -> pr "$%s = " n + | RIntBool (n, m) -> pr "($%s, $%s) = " n m | RStringList n | RPVList n | RVGList n @@ -2249,10 +2504,7 @@ and generate_perl_prototype name style = fun arg -> if !comma then pr ", "; comma := true; - match arg with - | String n -> pr "%s" n - | OptString n -> pr "%s" n - | Bool n -> pr "%s" n + pr "%s" (name_of_argt arg) ) (snd style); pr ");" diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index ebde5fca..0bb868ca 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -1111,3 +1111,865 @@ char **guestfs_read_lines (guestfs_h *g, return rv.ret.lines.lines_val; } +struct aug_init_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void aug_init_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_init_rv *rv = (struct aug_init_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_init: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_init: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_init (guestfs_h *g, + const char *root, + int flags) +{ + struct guestfs_aug_init_args args; + struct aug_init_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_init called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.root = (char *) root; + args.flags = flags; + serial = dispatch (g, GUESTFS_PROC_AUG_INIT, + (xdrproc_t) xdr_guestfs_aug_init_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_init_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_init failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_INIT, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct aug_close_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void aug_close_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_close_rv *rv = (struct aug_close_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_close: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_close: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_close (guestfs_h *g) +{ + struct aug_close_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_close called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + serial = dispatch (g, GUESTFS_PROC_AUG_CLOSE, NULL, NULL); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_close_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_close failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_CLOSE, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct aug_defvar_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_aug_defvar_ret ret; +}; + +static void aug_defvar_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_defvar_rv *rv = (struct aug_defvar_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_defvar: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_defvar: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_aug_defvar_ret (xdr, &rv->ret)) { + error (g, "guestfs_aug_defvar: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_defvar (guestfs_h *g, + const char *name, + const char *expr) +{ + struct guestfs_aug_defvar_args args; + struct aug_defvar_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_defvar called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.name = (char *) name; + args.expr = expr ? (char **) &expr : NULL; + serial = dispatch (g, GUESTFS_PROC_AUG_DEFVAR, + (xdrproc_t) xdr_guestfs_aug_defvar_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_defvar_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_defvar failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_DEFVAR, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return rv.ret.nrnodes; +} + +struct aug_defnode_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_aug_defnode_ret ret; +}; + +static void aug_defnode_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_defnode_rv *rv = (struct aug_defnode_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_defnode: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_defnode: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_aug_defnode_ret (xdr, &rv->ret)) { + error (g, "guestfs_aug_defnode: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +struct guestfs_int_bool *guestfs_aug_defnode (guestfs_h *g, + const char *name, + const char *expr, + const char *val) +{ + struct guestfs_aug_defnode_args args; + struct aug_defnode_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_defnode called from the wrong state, %d != READY", + g->state); + return NULL; + } + + memset (&rv, 0, sizeof rv); + + args.name = (char *) name; + args.expr = (char *) expr; + args.val = (char *) val; + serial = dispatch (g, GUESTFS_PROC_AUG_DEFNODE, + (xdrproc_t) xdr_guestfs_aug_defnode_args, (char *) &args); + if (serial == -1) + return NULL; + + rv.cb_done = 0; + g->reply_cb_internal = aug_defnode_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_defnode failed, see earlier error messages"); + return NULL; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_DEFNODE, serial) == -1) + return NULL; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return NULL; + } + + /* caller with free this */ + return safe_memdup (g, &rv.ret, sizeof (rv.ret)); +} + +struct aug_get_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_aug_get_ret ret; +}; + +static void aug_get_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_get_rv *rv = (struct aug_get_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_get: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_get: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_aug_get_ret (xdr, &rv->ret)) { + error (g, "guestfs_aug_get: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +char *guestfs_aug_get (guestfs_h *g, + const char *path) +{ + struct guestfs_aug_get_args args; + struct aug_get_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_get called from the wrong state, %d != READY", + g->state); + return NULL; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + serial = dispatch (g, GUESTFS_PROC_AUG_GET, + (xdrproc_t) xdr_guestfs_aug_get_args, (char *) &args); + if (serial == -1) + return NULL; + + rv.cb_done = 0; + g->reply_cb_internal = aug_get_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_get failed, see earlier error messages"); + return NULL; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_GET, serial) == -1) + return NULL; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return NULL; + } + + return rv.ret.val; /* caller will free */ +} + +struct aug_set_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void aug_set_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_set_rv *rv = (struct aug_set_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_set: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_set: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_set (guestfs_h *g, + const char *path, + const char *val) +{ + struct guestfs_aug_set_args args; + struct aug_set_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_set called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + args.val = (char *) val; + serial = dispatch (g, GUESTFS_PROC_AUG_SET, + (xdrproc_t) xdr_guestfs_aug_set_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_set_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_set failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_SET, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct aug_insert_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void aug_insert_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_insert_rv *rv = (struct aug_insert_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_insert: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_insert: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_insert (guestfs_h *g, + const char *path, + const char *label, + int before) +{ + struct guestfs_aug_insert_args args; + struct aug_insert_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_insert called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + args.label = (char *) label; + args.before = before; + serial = dispatch (g, GUESTFS_PROC_AUG_INSERT, + (xdrproc_t) xdr_guestfs_aug_insert_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_insert_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_insert failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_INSERT, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct aug_rm_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_aug_rm_ret ret; +}; + +static void aug_rm_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_rm_rv *rv = (struct aug_rm_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_rm: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_rm: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_aug_rm_ret (xdr, &rv->ret)) { + error (g, "guestfs_aug_rm: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_rm (guestfs_h *g, + const char *path) +{ + struct guestfs_aug_rm_args args; + struct aug_rm_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_rm called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + serial = dispatch (g, GUESTFS_PROC_AUG_RM, + (xdrproc_t) xdr_guestfs_aug_rm_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_rm_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_rm failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_RM, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return rv.ret.nrnodes; +} + +struct aug_mv_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void aug_mv_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_mv_rv *rv = (struct aug_mv_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_mv: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_mv: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_mv (guestfs_h *g, + const char *src, + const char *dest) +{ + struct guestfs_aug_mv_args args; + struct aug_mv_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_mv called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + args.src = (char *) src; + args.dest = (char *) dest; + serial = dispatch (g, GUESTFS_PROC_AUG_MV, + (xdrproc_t) xdr_guestfs_aug_mv_args, (char *) &args); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_mv_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_mv failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_MV, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct aug_match_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_aug_match_ret ret; +}; + +static void aug_match_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_match_rv *rv = (struct aug_match_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_match: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_match: failed to parse reply error"); + return; + } + goto done; + } + if (!xdr_guestfs_aug_match_ret (xdr, &rv->ret)) { + error (g, "guestfs_aug_match: failed to parse reply"); + return; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +char **guestfs_aug_match (guestfs_h *g, + const char *path) +{ + struct guestfs_aug_match_args args; + struct aug_match_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_match called from the wrong state, %d != READY", + g->state); + return NULL; + } + + memset (&rv, 0, sizeof rv); + + args.path = (char *) path; + serial = dispatch (g, GUESTFS_PROC_AUG_MATCH, + (xdrproc_t) xdr_guestfs_aug_match_args, (char *) &args); + if (serial == -1) + return NULL; + + rv.cb_done = 0; + g->reply_cb_internal = aug_match_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_match failed, see earlier error messages"); + return NULL; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_MATCH, serial) == -1) + return NULL; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return NULL; + } + + /* caller will free this, but we need to add a NULL entry */ + rv.ret.matches.matches_val = safe_realloc (g, rv.ret.matches.matches_val, + sizeof (char *) * (rv.ret.matches.matches_len + 1)); + rv.ret.matches.matches_val[rv.ret.matches.matches_len] = NULL; + return rv.ret.matches.matches_val; +} + +struct aug_save_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void aug_save_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_save_rv *rv = (struct aug_save_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_save: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_save: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_save (guestfs_h *g) +{ + struct aug_save_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_save called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + serial = dispatch (g, GUESTFS_PROC_AUG_SAVE, NULL, NULL); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_save_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_save failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_SAVE, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + +struct aug_load_rv { + int cb_done; /* flag to indicate callback was called */ + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void aug_load_cb (guestfs_h *g, void *data, XDR *xdr) +{ + struct aug_load_rv *rv = (struct aug_load_rv *) data; + + if (!xdr_guestfs_message_header (xdr, &rv->hdr)) { + error (g, "guestfs_aug_load: failed to parse reply header"); + return; + } + if (rv->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &rv->err)) { + error (g, "guestfs_aug_load: failed to parse reply error"); + return; + } + goto done; + } + done: + rv->cb_done = 1; + main_loop.main_loop_quit (g); +} + +int guestfs_aug_load (guestfs_h *g) +{ + struct aug_load_rv rv; + int serial; + + if (g->state != READY) { + error (g, "guestfs_aug_load called from the wrong state, %d != READY", + g->state); + return -1; + } + + memset (&rv, 0, sizeof rv); + + serial = dispatch (g, GUESTFS_PROC_AUG_LOAD, NULL, NULL); + if (serial == -1) + return -1; + + rv.cb_done = 0; + g->reply_cb_internal = aug_load_cb; + g->reply_cb_internal_data = &rv; + main_loop.main_loop_run (g); + g->reply_cb_internal = NULL; + g->reply_cb_internal_data = NULL; + if (!rv.cb_done) { + error (g, "guestfs_aug_load failed, see earlier error messages"); + return -1; + } + + if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_AUG_LOAD, serial) == -1) + return -1; + + if (rv.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", rv.err.error); + return -1; + } + + return 0; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index a84488d6..eeb69035 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -46,3 +46,15 @@ extern struct guestfs_lvm_pv_list *guestfs_pvs_full (guestfs_h *handle); extern struct guestfs_lvm_vg_list *guestfs_vgs_full (guestfs_h *handle); extern struct guestfs_lvm_lv_list *guestfs_lvs_full (guestfs_h *handle); extern char **guestfs_read_lines (guestfs_h *handle, const char *path); +extern int guestfs_aug_init (guestfs_h *handle, const char *root, int flags); +extern int guestfs_aug_close (guestfs_h *handle); +extern int guestfs_aug_defvar (guestfs_h *handle, const char *name, const char *expr); +extern struct guestfs_int_bool *guestfs_aug_defnode (guestfs_h *handle, const char *name, const char *expr, const char *val); +extern char *guestfs_aug_get (guestfs_h *handle, const char *path); +extern int guestfs_aug_set (guestfs_h *handle, const char *path, const char *val); +extern int guestfs_aug_insert (guestfs_h *handle, const char *path, const char *label, int before); +extern int guestfs_aug_rm (guestfs_h *handle, const char *path); +extern int guestfs_aug_mv (guestfs_h *handle, const char *src, const char *dest); +extern char **guestfs_aug_match (guestfs_h *handle, const char *path); +extern int guestfs_aug_save (guestfs_h *handle); +extern int guestfs_aug_load (guestfs_h *handle); diff --git a/src/guestfs-structs.h b/src/guestfs-structs.h index 45ce1316..b3751cd5 100644 --- a/src/guestfs-structs.h +++ b/src/guestfs-structs.h @@ -19,6 +19,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +struct guestfs_int_bool { + int32_t i; + int32_t b; +}; + struct guestfs_lvm_pv { char *pv_name; char pv_uuid[32]; /* this is NOT nul-terminated, be careful when printing */ diff --git a/src/guestfs.c b/src/guestfs.c index 7f0f8214..cab264a4 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -1271,6 +1271,12 @@ check_reply_header (guestfs_h *g, * generator.ml. */ void +guestfs_free_int_bool (struct guestfs_int_bool *x) +{ + free (x); +} + +void guestfs_free_lvm_pv_list (struct guestfs_lvm_pv_list *x) { xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_pv_list, (char *) x); diff --git a/src/guestfs.h b/src/guestfs.h index 1ed066e9..c677730a 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -45,6 +45,7 @@ extern guestfs_abort_cb guestfs_get_out_of_memory_handler (guestfs_h *g); #include <guestfs-structs.h> #include <guestfs-actions.h> +extern void guestfs_free_int_bool (struct guestfs_int_bool *); extern void guestfs_free_lvm_pv_list (struct guestfs_lvm_pv_list *); extern void guestfs_free_lvm_vg_list (struct guestfs_lvm_vg_list *); extern void guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index 31034e54..a5737288 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -363,6 +363,165 @@ xdr_guestfs_read_lines_ret (XDR *xdrs, guestfs_read_lines_ret *objp) } bool_t +xdr_guestfs_aug_init_args (XDR *xdrs, guestfs_aug_init_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->root, ~0)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_defvar_args (XDR *xdrs, guestfs_aug_defvar_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->name, ~0)) + return FALSE; + if (!xdr_pointer (xdrs, (char **)&objp->expr, sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_defvar_ret (XDR *xdrs, guestfs_aug_defvar_ret *objp) +{ + register int32_t *buf; + + if (!xdr_int (xdrs, &objp->nrnodes)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_defnode_args (XDR *xdrs, guestfs_aug_defnode_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->name, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->expr, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->val, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_defnode_ret (XDR *xdrs, guestfs_aug_defnode_ret *objp) +{ + register int32_t *buf; + + if (!xdr_int (xdrs, &objp->nrnodes)) + return FALSE; + if (!xdr_bool (xdrs, &objp->created)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_get_args (XDR *xdrs, guestfs_aug_get_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_get_ret (XDR *xdrs, guestfs_aug_get_ret *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->val, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_set_args (XDR *xdrs, guestfs_aug_set_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->val, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_insert_args (XDR *xdrs, guestfs_aug_insert_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->label, ~0)) + return FALSE; + if (!xdr_bool (xdrs, &objp->before)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_rm_args (XDR *xdrs, guestfs_aug_rm_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_rm_ret (XDR *xdrs, guestfs_aug_rm_ret *objp) +{ + register int32_t *buf; + + if (!xdr_int (xdrs, &objp->nrnodes)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_mv_args (XDR *xdrs, guestfs_aug_mv_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->src, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->dest, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_match_args (XDR *xdrs, guestfs_aug_match_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_aug_match_ret (XDR *xdrs, guestfs_aug_match_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->matches.matches_val, (u_int *) &objp->matches.matches_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { register int32_t *buf; diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index a2bf0c13..94129864 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -204,6 +204,88 @@ struct guestfs_read_lines_ret { }; typedef struct guestfs_read_lines_ret guestfs_read_lines_ret; +struct guestfs_aug_init_args { + char *root; + int flags; +}; +typedef struct guestfs_aug_init_args guestfs_aug_init_args; + +struct guestfs_aug_defvar_args { + char *name; + str *expr; +}; +typedef struct guestfs_aug_defvar_args guestfs_aug_defvar_args; + +struct guestfs_aug_defvar_ret { + int nrnodes; +}; +typedef struct guestfs_aug_defvar_ret guestfs_aug_defvar_ret; + +struct guestfs_aug_defnode_args { + char *name; + char *expr; + char *val; +}; +typedef struct guestfs_aug_defnode_args guestfs_aug_defnode_args; + +struct guestfs_aug_defnode_ret { + int nrnodes; + bool_t created; +}; +typedef struct guestfs_aug_defnode_ret guestfs_aug_defnode_ret; + +struct guestfs_aug_get_args { + char *path; +}; +typedef struct guestfs_aug_get_args guestfs_aug_get_args; + +struct guestfs_aug_get_ret { + char *val; +}; +typedef struct guestfs_aug_get_ret guestfs_aug_get_ret; + +struct guestfs_aug_set_args { + char *path; + char *val; +}; +typedef struct guestfs_aug_set_args guestfs_aug_set_args; + +struct guestfs_aug_insert_args { + char *path; + char *label; + bool_t before; +}; +typedef struct guestfs_aug_insert_args guestfs_aug_insert_args; + +struct guestfs_aug_rm_args { + char *path; +}; +typedef struct guestfs_aug_rm_args guestfs_aug_rm_args; + +struct guestfs_aug_rm_ret { + int nrnodes; +}; +typedef struct guestfs_aug_rm_ret guestfs_aug_rm_ret; + +struct guestfs_aug_mv_args { + char *src; + char *dest; +}; +typedef struct guestfs_aug_mv_args guestfs_aug_mv_args; + +struct guestfs_aug_match_args { + char *path; +}; +typedef struct guestfs_aug_match_args guestfs_aug_match_args; + +struct guestfs_aug_match_ret { + struct { + u_int matches_len; + str *matches_val; + } matches; +}; +typedef struct guestfs_aug_match_ret guestfs_aug_match_ret; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -220,7 +302,19 @@ enum guestfs_procedure { GUESTFS_PROC_VGS_FULL = 13, GUESTFS_PROC_LVS_FULL = 14, GUESTFS_PROC_READ_LINES = 15, - GUESTFS_PROC_dummy = 15 + 1, + GUESTFS_PROC_AUG_INIT = 16, + GUESTFS_PROC_AUG_CLOSE = 26, + GUESTFS_PROC_AUG_DEFVAR = 17, + GUESTFS_PROC_AUG_DEFNODE = 18, + GUESTFS_PROC_AUG_GET = 19, + GUESTFS_PROC_AUG_SET = 20, + GUESTFS_PROC_AUG_INSERT = 21, + GUESTFS_PROC_AUG_RM = 22, + GUESTFS_PROC_AUG_MV = 23, + GUESTFS_PROC_AUG_MATCH = 24, + GUESTFS_PROC_AUG_SAVE = 25, + GUESTFS_PROC_AUG_LOAD = 27, + GUESTFS_PROC_dummy = 27 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -283,6 +377,20 @@ extern bool_t xdr_guestfs_vgs_full_ret (XDR *, guestfs_vgs_full_ret*); extern bool_t xdr_guestfs_lvs_full_ret (XDR *, guestfs_lvs_full_ret*); extern bool_t xdr_guestfs_read_lines_args (XDR *, guestfs_read_lines_args*); extern bool_t xdr_guestfs_read_lines_ret (XDR *, guestfs_read_lines_ret*); +extern bool_t xdr_guestfs_aug_init_args (XDR *, guestfs_aug_init_args*); +extern bool_t xdr_guestfs_aug_defvar_args (XDR *, guestfs_aug_defvar_args*); +extern bool_t xdr_guestfs_aug_defvar_ret (XDR *, guestfs_aug_defvar_ret*); +extern bool_t xdr_guestfs_aug_defnode_args (XDR *, guestfs_aug_defnode_args*); +extern bool_t xdr_guestfs_aug_defnode_ret (XDR *, guestfs_aug_defnode_ret*); +extern bool_t xdr_guestfs_aug_get_args (XDR *, guestfs_aug_get_args*); +extern bool_t xdr_guestfs_aug_get_ret (XDR *, guestfs_aug_get_ret*); +extern bool_t xdr_guestfs_aug_set_args (XDR *, guestfs_aug_set_args*); +extern bool_t xdr_guestfs_aug_insert_args (XDR *, guestfs_aug_insert_args*); +extern bool_t xdr_guestfs_aug_rm_args (XDR *, guestfs_aug_rm_args*); +extern bool_t xdr_guestfs_aug_rm_ret (XDR *, guestfs_aug_rm_ret*); +extern bool_t xdr_guestfs_aug_mv_args (XDR *, guestfs_aug_mv_args*); +extern bool_t xdr_guestfs_aug_match_args (XDR *, guestfs_aug_match_args*); +extern bool_t xdr_guestfs_aug_match_ret (XDR *, guestfs_aug_match_ret*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -315,6 +423,20 @@ extern bool_t xdr_guestfs_vgs_full_ret (); extern bool_t xdr_guestfs_lvs_full_ret (); extern bool_t xdr_guestfs_read_lines_args (); extern bool_t xdr_guestfs_read_lines_ret (); +extern bool_t xdr_guestfs_aug_init_args (); +extern bool_t xdr_guestfs_aug_defvar_args (); +extern bool_t xdr_guestfs_aug_defvar_ret (); +extern bool_t xdr_guestfs_aug_defnode_args (); +extern bool_t xdr_guestfs_aug_defnode_ret (); +extern bool_t xdr_guestfs_aug_get_args (); +extern bool_t xdr_guestfs_aug_get_ret (); +extern bool_t xdr_guestfs_aug_set_args (); +extern bool_t xdr_guestfs_aug_insert_args (); +extern bool_t xdr_guestfs_aug_rm_args (); +extern bool_t xdr_guestfs_aug_rm_ret (); +extern bool_t xdr_guestfs_aug_mv_args (); +extern bool_t xdr_guestfs_aug_match_args (); +extern bool_t xdr_guestfs_aug_match_ret (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index c00c4c4d..eb3045e0 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -85,23 +85,15 @@ struct guestfs_lvm_int_lv { typedef struct guestfs_lvm_int_lv guestfs_lvm_int_lv_list<>; -/* guestfs_mount */ - struct guestfs_mount_args { string device<>; string mountpoint<>; }; -/* guestfs_sync */ - -/* guestfs_touch */ - struct guestfs_touch_args { string path<>; }; -/* guestfs_cat */ - struct guestfs_cat_args { string path<>; }; @@ -110,8 +102,6 @@ struct guestfs_cat_ret { string content<>; }; -/* guestfs_ll */ - struct guestfs_ll_args { string directory<>; }; @@ -120,8 +110,6 @@ struct guestfs_ll_ret { string listing<>; }; -/* guestfs_ls */ - struct guestfs_ls_args { string directory<>; }; @@ -130,56 +118,38 @@ struct guestfs_ls_ret { str listing<>; }; -/* guestfs_list_devices */ - struct guestfs_list_devices_ret { str devices<>; }; -/* guestfs_list_partitions */ - struct guestfs_list_partitions_ret { str partitions<>; }; -/* guestfs_pvs */ - struct guestfs_pvs_ret { str physvols<>; }; -/* guestfs_vgs */ - struct guestfs_vgs_ret { str volgroups<>; }; -/* guestfs_lvs */ - struct guestfs_lvs_ret { str logvols<>; }; -/* guestfs_pvs_full */ - struct guestfs_pvs_full_ret { guestfs_lvm_int_pv_list physvols; }; -/* guestfs_vgs_full */ - struct guestfs_vgs_full_ret { guestfs_lvm_int_vg_list volgroups; }; -/* guestfs_lvs_full */ - struct guestfs_lvs_full_ret { guestfs_lvm_int_lv_list logvols; }; -/* guestfs_read_lines */ - struct guestfs_read_lines_args { string path<>; }; @@ -188,6 +158,71 @@ struct guestfs_read_lines_ret { str lines<>; }; +struct guestfs_aug_init_args { + string root<>; + int flags; +}; + +struct guestfs_aug_defvar_args { + string name<>; + str *expr; +}; + +struct guestfs_aug_defvar_ret { + int nrnodes; +}; + +struct guestfs_aug_defnode_args { + string name<>; + string expr<>; + string val<>; +}; + +struct guestfs_aug_defnode_ret { + int nrnodes; + bool created; +}; + +struct guestfs_aug_get_args { + string path<>; +}; + +struct guestfs_aug_get_ret { + string val<>; +}; + +struct guestfs_aug_set_args { + string path<>; + string val<>; +}; + +struct guestfs_aug_insert_args { + string path<>; + string label<>; + bool before; +}; + +struct guestfs_aug_rm_args { + string path<>; +}; + +struct guestfs_aug_rm_ret { + int nrnodes; +}; + +struct guestfs_aug_mv_args { + string src<>; + string dest<>; +}; + +struct guestfs_aug_match_args { + string path<>; +}; + +struct guestfs_aug_match_ret { + str matches<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -204,6 +239,18 @@ enum guestfs_procedure { GUESTFS_PROC_VGS_FULL = 13, GUESTFS_PROC_LVS_FULL = 14, GUESTFS_PROC_READ_LINES = 15, + GUESTFS_PROC_AUG_INIT = 16, + GUESTFS_PROC_AUG_CLOSE = 26, + GUESTFS_PROC_AUG_DEFVAR = 17, + GUESTFS_PROC_AUG_DEFNODE = 18, + GUESTFS_PROC_AUG_GET = 19, + GUESTFS_PROC_AUG_SET = 20, + GUESTFS_PROC_AUG_INSERT = 21, + GUESTFS_PROC_AUG_RM = 22, + GUESTFS_PROC_AUG_MV = 23, + GUESTFS_PROC_AUG_MATCH = 24, + GUESTFS_PROC_AUG_SAVE = 25, + GUESTFS_PROC_AUG_LOAD = 27, GUESTFS_PROC_dummy }; |