Richard W.M. Jones
2009-Nov-20 15:11 UTC
[Libguestfs] [PATCH 0/2] Implement guestfs_available API.
These two patches are the implementation of the guestfs_available API. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming blog: http://rwmj.wordpress.com Fedora now supports 80 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
Richard W.M. Jones
2009-Nov-20 15:12 UTC
[Libguestfs] [PATCH 1/2] availability: Add guestfs_available.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html -------------- next part -------------->From e24b0b7fb4160af4c82107c55176353abc07b69a Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Fri, 20 Nov 2009 13:06:49 +0000 Subject: [PATCH 1/2] availability: Add guestfs_available. Start a new API allowing groups of functions to be tested for availability. There are two reasons for this: (1) If libguestfs is built with missing dependencies (eg. no Augeas lib) then the corresponding functions are disabled in the appliance. Up till now there has been no way to test for this except to speculatively issue commands and check for errors. (2) When we port the daemon to Win32 it is likely that major pieces of functionality won't be available (eg. LVM support). This API gives a way to test for that. There is no change for existing clients: you still have to check for errors from individual API calls. For new clients, you will be able to test for availability of particular APIs. Usage scenario (A): An LVM editing tool which requires both the LVM API and inotify in order to function at all: char *apis[] = { "inotify", "lvm2", NULL }; r = guestfs_available (g, apis); if (r == -1) { /* print an error and exit */ } Usage scenario (B): A general purpose tool which optionally provides configuration file editing, but this can be disabled, the result merely being reduced functionality: char *apis[] = { "augeas", NULL }; r = guestfs_available (g, apis); enable_config_edit_menus = r == 0; --- daemon/Makefile.am | 1 + daemon/available.c | 38 ++++++++++++++++++++++++++++++++++++++ po/POTFILES.in | 1 + src/MAX_PROC_NR | 2 +- src/generator.ml | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 1 deletions(-) create mode 100644 daemon/available.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 1716c2f..d851e52 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -41,6 +41,7 @@ $(libsrcdir)/guestfs_protocol.o: force noinst_PROGRAMS = guestfsd guestfsd_SOURCES = \ actions.h \ + available.c \ augeas.c \ blkid.c \ blockdev.c \ diff --git a/daemon/available.c b/daemon/available.c new file mode 100644 index 0000000..b43d182 --- /dev/null +++ b/daemon/available.c @@ -0,0 +1,38 @@ +/* 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 "../src/guestfs_protocol.h" +#include "daemon.h" +#include "actions.h" + +int +do_available (char *const *groups) +{ + if (groups[0] != NULL) { + reply_with_error ("%s: unknown group", groups[0]); + return -1; + } + + return 0; +} diff --git a/po/POTFILES.in b/po/POTFILES.in index cf632fc..2c7998d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,5 @@ daemon/augeas.c +daemon/available.c daemon/blkid.c daemon/blockdev.c daemon/checksum.c diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index c34a804..a817176 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -215 +216 diff --git a/src/generator.ml b/src/generator.ml index 6bc9b28..def9f8c 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -4102,6 +4102,52 @@ must be a number in the range C<[0..255]>. To fill a file with zero bytes (sparsely), it is much more efficient to use C<guestfs_truncate_size>."); + ("available", (RErr, [StringList "groups"]), 216, [], + [], + "test availability of some parts of the API", + "\ +This command is used to check the availability of some +groups of libguestfs functions which not all builds of +libguestfs will be able to provide. + +The precise libguestfs function groups that may be checked by this +command are listed in L<guestfs(3)/AVAILABILITY>. + +The argument C<groups> is a list of API group names, eg: +C<[\"inotify\", \"part\"]> would check for the availability of +the C<guestfs_inotify_*> functions and C<guestfs_part_*> +(partition editing) functions. + +The command returns no error if I<all> requested groups are available. + +It returns an error if one or more of the requested +groups is unavailable. + +If an unknown group name is included in the +list of C<groups> then an error is always returned. + +I<Notes:> + +You must call C<guestfs_launch> before calling this function. +The reason is because we don't know what function groups are +supported by the appliance/daemon until it is running and can +be queried. + +If a group of functions is available, this does not necessarily +mean that they will work. You still have to check for errors +when calling individual API functions even if they are +available. + +It is usually the job of distro packagers to build +complete functionality into the libguestfs appliance. +Upstream libguestfs, if built from source with all +requirements satisfied, will support everything. + +This call was added in version C<1.0.80>. In previous +versions of libguestfs all you could do would be to speculatively +execute a command to find out if the daemon implemented it. +See also C<guestfs_version>."); + ] let all_functions = non_daemon_functions @ daemon_functions -- 1.6.5.2
Richard W.M. Jones
2009-Nov-20 15:13 UTC
[Libguestfs] [PATCH 2/2] availability: Add optional groups and implement guestfs_available call.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v -------------- next part -------------->From af24922c2c01dda6c343d8dfb6133e1d37a8781e Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Fri, 20 Nov 2009 14:10:06 +0000 Subject: [PATCH 2/2] availability: Add optional groups and implement guestfs_available call. The current groups are defined very conservatively using the following criteria: (a) Would be impossible to implement on Windows because of sheer architectural differences (eg: mknod). (b) Already optional (augeas, inotify). (c) Not currently optional but not implemented on older RHEL and Debian releases (ntfs-3g.probe, scrub, zerofree). The optional groups I've defined according to these criteria are: . augeas . inotify . linuxmodules . linuxxattrs . lvm2 . mknod . ntfs3g . scrub . selinux . zerofree (Note that these choices don't prevent us from adding more optional groups in future. On the other hand to avoid breaking ABIs we would not wish to change the above groups). The rest of this large commit is really just implementation: Each optional function is classified using Optional "group" flag in the generator. The daemon has to implement a function int optgroup_<name>_available (void); for each optional group. Some of these functions are fixed at compile time, and some do simple run-time tests. The do_available implementation in the daemon looks up the correct function in a table and runs it. We document the optional groups in the guestfs(3) man page. Also: I added a NOT_AVAILABLE macro in order to unify all the existing places where we had a message equivalent to "function __func__ is not available". --- .gitignore | 3 + Makefile.am | 34 +++++++--- daemon/Makefile.am | 2 + daemon/augeas.c | 52 +++++++------- daemon/available.c | 23 ++++++- daemon/daemon.h | 17 +++++ daemon/inotify.c | 31 +++++---- daemon/lvm.c | 8 ++ daemon/mknod.c | 18 +++++- daemon/modprobe.c | 8 ++ daemon/ntfs.c | 8 ++ daemon/scrub.c | 8 ++ daemon/selinux.c | 21 +++++- daemon/xattr.c | 47 +++++++------ daemon/zerofree.c | 8 ++ guestfs.pod | 7 ++ po/POTFILES.in | 1 + src/generator.ml | 187 ++++++++++++++++++++++++++++++++++++---------------- 18 files changed, 349 insertions(+), 134 deletions(-) diff --git a/.gitignore b/.gitignore index b3869c5..3348b97 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,8 @@ daemon/guestfsd daemon/install-sh daemon/missing daemon/names.c +daemon/optgroups.c +daemon/optgroups.h daemon/stubs.c depcomp .deps @@ -65,6 +67,7 @@ guestfish.1 guestfish-actions.pod guestfs.3 guestfs-actions.pod +guestfs-availability.pod guestfs-structs.pod haskell/Bindtests haskell/Bindtests.hs diff --git a/Makefile.am b/Makefile.am index 9332a10..7e863f5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,8 +57,9 @@ SUBDIRS += haskell endif generator_built = \ - guestfs-structs.pod \ guestfs-actions.pod \ + guestfs-availability.pod \ + guestfs-structs.pod \ guestfish-actions.pod EXTRA_DIST = \ @@ -83,15 +84,23 @@ EXTRA_DIST = \ libguestfs.3 # Manual pages. -# guestfs-actions.pod and guestfs-structs are autogenerated. There is -# no include mechanism for POD, so we have to do it by hand. +# guestfs-actions.pod, guestfs-availability.pod and guestfs-structs +# are autogenerated. There is no include mechanism for POD, so we +# have to do it by hand. man_MANS = guestfs.3 libguestfs.3 guestfish.1 -guestfs.3: guestfs.pod guestfs-actions.pod guestfs-structs.pod +guestfs.3: guestfs.pod \ + guestfs-actions.pod \ + guestfs-availability.pod \ + guestfs-structs.pod sed \ - -e '/@ACTIONS@/rguestfs-actions.pod' -e 's/@ACTIONS@//' \ - -e '/@STRUCTS@/rguestfs-structs.pod' -e 's/@STRUCTS@//' \ + -e '/@ACTIONS@/rguestfs-actions.pod' \ + -e 's/@ACTIONS@//' \ + -e '/@AVAILABILITY@/rguestfs-availability.pod' \ + -e 's/@AVAILABILITY@//' \ + -e '/@STRUCTS@/rguestfs-structs.pod' \ + -e 's/@STRUCTS@//' \ < $< | \ $(POD2MAN) \ --section 3 \ @@ -113,11 +122,18 @@ guestfish.1: guestfish.pod guestfish-actions.pod noinst_DATA = html/guestfs.3.html html/guestfish.1.html -html/guestfs.3.html: guestfs.pod guestfs-actions.pod guestfs-structs.pod +html/guestfs.3.html: guestfs.pod \ + guestfs-actions.pod \ + guestfs-availability.pod \ + guestfs-structs.pod mkdir -p html sed \ - -e '/@ACTIONS@/rguestfs-actions.pod' -e 's/@ACTIONS@//' \ - -e '/@STRUCTS@/rguestfs-structs.pod' -e 's/@STRUCTS@//' \ + -e '/@ACTIONS@/rguestfs-actions.pod' \ + -e 's/@ACTIONS@//' \ + -e '/@AVAILABILITY@/rguestfs-availability.pod' \ + -e 's/@AVAILABILITY@//' \ + -e '/@STRUCTS@/rguestfs-structs.pod' \ + -e 's/@STRUCTS@//' \ < $< | \ pod2html \ --css 'pod.css' \ diff --git a/daemon/Makefile.am b/daemon/Makefile.am index d851e52..9fa8dda 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -81,6 +81,8 @@ guestfsd_SOURCES = \ mount.c \ names.c \ ntfs.c \ + optgroups.c \ + optgroups.h \ parted.c \ pingdaemon.c \ proto.c \ diff --git a/daemon/augeas.c b/daemon/augeas.c index b56012c..be53d29 100644 --- a/daemon/augeas.c +++ b/daemon/augeas.c @@ -29,6 +29,7 @@ #include "daemon.h" #include "actions.h" +#include "optgroups.h" #ifdef HAVE_AUGEAS /* The Augeas handle. We maintain a single handle per daemon, which @@ -45,6 +46,18 @@ static augeas *aug = NULL; } \ } \ while (0) + +int +optgroup_augeas_available (void) +{ + return 1; +} +#else /* !HAVE_AUGEAS */ +int +optgroup_augeas_available (void) +{ + return 0; +} #endif /* We need to rewrite the root path so it is based at /sysroot. */ @@ -75,8 +88,7 @@ do_aug_init (const char *root, int flags) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -91,8 +103,7 @@ do_aug_close (void) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -111,8 +122,7 @@ do_aug_defvar (const char *name, const char *expr) } return r; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -133,8 +143,7 @@ do_aug_defnode (const char *name, const char *expr, const char *val) r.b = created; return &r; #else - reply_with_error ("%s is not available", __func__); - return NULL; + NOT_AVAILABLE (-1); #endif } @@ -176,8 +185,7 @@ do_aug_get (const char *path) return v; /* Caller frees. */ #else - reply_with_error ("%s is not available", __func__); - return NULL; + NOT_AVAILABLE (NULL); #endif } @@ -197,8 +205,7 @@ do_aug_set (const char *path, const char *val) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -218,8 +225,7 @@ do_aug_insert (const char *path, const char *label, int before) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -239,8 +245,7 @@ do_aug_rm (const char *path) return r; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -260,8 +265,7 @@ do_aug_mv (const char *src, const char *dest) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -295,8 +299,7 @@ do_aug_match (const char *path) return matches; /* Caller frees. */ #else - reply_with_error ("%s is not available", __func__); - return NULL; + NOT_AVAILABLE (NULL); #endif } @@ -313,8 +316,7 @@ do_aug_save (void) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -331,8 +333,7 @@ do_aug_load (void) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -379,7 +380,6 @@ do_aug_ls (const char *path) sort_strings (matches, count_strings ((void *) matches)); return matches; /* Caller frees. */ #else - reply_with_error ("%s is not available", __func__); - return NULL; + NOT_AVAILABLE (NULL); #endif } diff --git a/daemon/available.c b/daemon/available.c index b43d182..77fdfa2 100644 --- a/daemon/available.c +++ b/daemon/available.c @@ -29,9 +29,26 @@ int do_available (char *const *groups) { - if (groups[0] != NULL) { - reply_with_error ("%s: unknown group", groups[0]); - return -1; + int av; + size_t i, j; + + for (i = 0; groups[i] != NULL; ++i) { + for (j = 0; optgroups[j].group != NULL; ++j) { + if (STREQ (groups[i], optgroups[j].group)) { + av = optgroups[j].available (); + if (!av) { + reply_with_error ("%s: group not available", optgroups[j].group); + return -1; + } + break; /* out of for (j) loop */ + } + } + + /* Unknown group? */ + if (optgroups[j].group == NULL) { + reply_with_error ("%s: unknown group", groups[i]); + return -1; + } } return 0; diff --git a/daemon/daemon.h b/daemon/daemon.h index ac68479..ad2a237 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -104,6 +104,13 @@ extern guestfs_int_lvm_lv_list *parse_command_line_lvs (void); /*-- in proto.c --*/ extern void main_loop (int sock) __attribute__((noreturn)); +/*-- in optgroups.c (auto-generated) --*/ +struct optgroup { + const char *group; /* Name of the optional group. */ + int (*available) (void); /* Function to test availability. */ +}; +extern struct optgroup optgroups[]; + /* ordinary daemon functions use these to indicate errors */ extern void reply_with_error (const char *fs, ...) __attribute__((format (printf,1,2))); @@ -224,6 +231,16 @@ extern void reply (xdrproc_t xdrp, char *ret); } \ while (0) +/* Marks functions which are not available. + * NB. Cannot be used for FileIn functions. + */ +#define NOT_AVAILABLE(errcode) \ + do { \ + reply_with_error ("%s: function not available", __func__); \ + return (errcode); \ + } \ + while (0) + #ifndef __attribute__ # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) # define __attribute__(x) /* empty */ diff --git a/daemon/inotify.c b/daemon/inotify.c index 465d0b6..24ce76e 100644 --- a/daemon/inotify.c +++ b/daemon/inotify.c @@ -31,6 +31,7 @@ #include "../src/guestfs_protocol.h" #include "daemon.h" #include "actions.h" +#include "optgroups.h" #ifdef HAVE_SYS_INOTIFY_H /* Currently open inotify handle, or -1 if not opened. */ @@ -38,6 +39,18 @@ static int inotify_fd = -1; static char inotify_buf[64*1024*1024]; /* Event buffer, [0..posn-1] is valid */ static size_t inotify_posn = 0; + +int +optgroup_inotify_available (void) +{ + return 1; +} +#else /* !HAVE_SYS_INOTIFY_H */ +int +optgroup_inotify_available (void) +{ + return 0; +} #endif /* Because inotify_init does NEED_ROOT, NEED_INOTIFY implies NEED_ROOT. */ @@ -106,8 +119,7 @@ do_inotify_init (int max_events) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -132,8 +144,7 @@ do_inotify_close (void) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -161,8 +172,7 @@ do_inotify_add_watch (const char *path, int mask) return r; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -179,8 +189,7 @@ do_inotify_rm_watch (int wd) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -294,8 +303,7 @@ do_inotify_read (void) free (ret); return NULL; #else - reply_with_error ("%s is not available", __func__); - return NULL; + NOT_AVAILABLE (NULL); #endif } @@ -371,7 +379,6 @@ do_inotify_files (void) unlink ("/tmp/inotify"); return NULL; #else - reply_with_error ("%s is not available", __func__); - return NULL; + NOT_AVAILABLE (NULL); #endif } diff --git a/daemon/lvm.c b/daemon/lvm.c index 3920e95..564517c 100644 --- a/daemon/lvm.c +++ b/daemon/lvm.c @@ -26,6 +26,14 @@ #include "daemon.h" #include "c-ctype.h" #include "actions.h" +#include "optgroups.h" + +int +optgroup_lvm2_available (void) +{ + int r = access ("/sbin/lvm", X_OK); + return r == 0; +} /* LVM actions. Keep an eye on liblvm, although at the time * of writing it hasn't progressed very far. diff --git a/daemon/mknod.c b/daemon/mknod.c index 6ff88ef..46a1839 100644 --- a/daemon/mknod.c +++ b/daemon/mknod.c @@ -29,6 +29,21 @@ #include "../src/guestfs_protocol.h" #include "daemon.h" #include "actions.h" +#include "optgroups.h" + +#ifdef HAVE_MKNOD +int +optgroup_mknod_available (void) +{ + return 1; +} +#else +int +optgroup_mknod_available (void) +{ + return 0; +} +#endif int do_mknod (int mode, int devmajor, int devminor, const char *path) @@ -47,8 +62,7 @@ do_mknod (int mode, int devmajor, int devminor, const char *path) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } diff --git a/daemon/modprobe.c b/daemon/modprobe.c index 7e3d1b7..ac62b34 100644 --- a/daemon/modprobe.c +++ b/daemon/modprobe.c @@ -23,6 +23,14 @@ #include "daemon.h" #include "actions.h" +#include "optgroups.h" + +int +optgroup_linuxmodules_available (void) +{ + int r = access ("/sbin/modprobe", X_OK); + return r == 0; +} int do_modprobe (const char *module) diff --git a/daemon/ntfs.c b/daemon/ntfs.c index 85deb65..b353008 100644 --- a/daemon/ntfs.c +++ b/daemon/ntfs.c @@ -25,6 +25,14 @@ #include "daemon.h" #include "actions.h" +#include "optgroups.h" + +int +optgroup_ntfs3g_available (void) +{ + int r = access ("/bin/ntfs-3g.probe", X_OK); + return r == 0; +} int do_ntfs_3g_probe (int rw, const char *device) diff --git a/daemon/scrub.c b/daemon/scrub.c index 15c8b6c..e37a1e1 100644 --- a/daemon/scrub.c +++ b/daemon/scrub.c @@ -26,6 +26,14 @@ #include "daemon.h" #include "actions.h" +#include "optgroups.h" + +int +optgroup_scrub_available (void) +{ + int r = access ("/usr/bin/scrub", X_OK); + return r == 0; +} int do_scrub_device (const char *device) diff --git a/daemon/selinux.c b/daemon/selinux.c index 3b4b9ba..e49e657 100644 --- a/daemon/selinux.c +++ b/daemon/selinux.c @@ -29,6 +29,21 @@ #include "../src/guestfs_protocol.h" #include "daemon.h" #include "actions.h" +#include "optgroups.h" + +#if defined(HAVE_LIBSELINUX) +int +optgroup_selinux_available (void) +{ + return 1; +} +#else /* !HAVE_LIBSELINUX */ +int +optgroup_selinux_available (void) +{ + return 0; +} +#endif /* !HAVE_LIBSELINUX */ /* setcon is only valid under the following circumstances: * - single threaded @@ -45,8 +60,7 @@ do_setcon (const char *context) return 0; #else - reply_with_error ("%s is not available", __func__); - return -1; + NOT_AVAILABLE (-1); #endif } @@ -71,7 +85,6 @@ do_getcon (void) return r; /* caller frees */ #else - reply_with_error ("%s is not available", __func__); - return NULL; + NOT_AVAILABLE (NULL); #endif } diff --git a/daemon/xattr.c b/daemon/xattr.c index c218dea..e58dc7e 100644 --- a/daemon/xattr.c +++ b/daemon/xattr.c @@ -24,16 +24,23 @@ #include "../src/guestfs_protocol.h" #include "daemon.h" #include "actions.h" +#include "optgroups.h" #if defined(HAVE_ATTR_XATTR_H) || defined(HAVE_SYS_XATTR_H) -#ifdef HAVE_ATTR_XATTR_H -#include <attr/xattr.h> -#else -#ifdef HAVE_SYS_XATTR_H -#include <sys/xattr.h> -#endif -#endif +# ifdef HAVE_ATTR_XATTR_H +# include <attr/xattr.h> +# else +# ifdef HAVE_SYS_XATTR_H +# include <sys/xattr.h> +# endif +# endif + +int +optgroup_linuxxattrs_available (void) +{ + return 1; +} static guestfs_int_xattr_list *getxattrs (const char *path, ssize_t (*listxattr) (const char *path, char *list, size_t size), ssize_t (*getxattr) (const char *path, const char *name, void *value, size_t size)); static int _setxattr (const char *xattr, const char *val, int vallen, const char *path, int (*setxattr) (const char *path, const char *name, const void *value, size_t size, int flags)); @@ -442,54 +449,52 @@ do_lxattrlist (const char *path, char *const *names) } #else /* no xattr.h */ +int +optgroup_linuxxattrs_available (void) +{ + return 0; +} guestfs_int_xattr_list * do_getxattrs (const char *path) { - reply_with_error ("getxattrs: no support for xattrs"); - return NULL; + NOT_AVAILABLE (NULL); } guestfs_int_xattr_list * do_lgetxattrs (const char *path) { - reply_with_error ("lgetxattrs: no support for xattrs"); - return NULL; + NOT_AVAILABLE (NULL); } int do_setxattr (const char *xattr, const char *val, int vallen, const char *path) { - reply_with_error ("setxattr: no support for xattrs"); - return -1; + NOT_AVAILABLE (-1); } int do_lsetxattr (const char *xattr, const char *val, int vallen, const char *path) { - reply_with_error ("lsetxattr: no support for xattrs"); - return -1; + NOT_AVAILABLE (-1); } int do_removexattr (const char *xattr, const char *path) { - reply_with_error ("removexattr: no support for xattrs"); - return -1; + NOT_AVAILABLE (-1); } int do_lremovexattr (const char *xattr, const char *path) { - reply_with_error ("lremovexattr: no support for xattrs"); - return -1; + NOT_AVAILABLE (-1); } guestfs_int_xattr_list * do_lxattrlist (const char *path, char *const *names) { - reply_with_error ("lxattrlist: no support for xattrs"); - return NULL; + NOT_AVAILABLE (NULL); } #endif /* no xattr.h */ diff --git a/daemon/zerofree.c b/daemon/zerofree.c index ffb9b70..05a547b 100644 --- a/daemon/zerofree.c +++ b/daemon/zerofree.c @@ -26,6 +26,14 @@ #include "daemon.h" #include "actions.h" +#include "optgroups.h" + +int +optgroup_zerofree_available (void) +{ + int r = access ("/usr/sbin/zerofree", X_OK); + return r == 0; +} int do_zerofree (const char *device) diff --git a/guestfs.pod b/guestfs.pod index 0e7079c..4a47733 100644 --- a/guestfs.pod +++ b/guestfs.pod @@ -623,6 +623,13 @@ developer to program in confidence against libguestfs. @STRUCTS@ +=head1 AVAILABILITY + +Using L</guestfs_available> you can test availability of +the following groups of functions: + + at AVAILABILITY@ + =head1 STATE MACHINE AND LOW-LEVEL EVENT API Internally, libguestfs is implemented by running a virtual machine diff --git a/po/POTFILES.in b/po/POTFILES.in index 2c7998d..7707811 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -37,6 +37,7 @@ daemon/modprobe.c daemon/mount.c daemon/names.c daemon/ntfs.c +daemon/optgroups.c daemon/parted.c daemon/pingdaemon.c daemon/proto.c diff --git a/src/generator.ml b/src/generator.ml index def9f8c..4099af8 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -175,6 +175,7 @@ type flags | NotInFish (* do not export via guestfish *) | NotInDocs (* do not add this function to documentation *) | DeprecatedBy of string (* function is deprecated, use .. instead *) + | Optional of string (* function is part of an optional group *) (* You can supply zero or as many tests as you want per API call. * @@ -978,7 +979,7 @@ The full partition device names are returned, eg. C</dev/sda1> This does not return logical volumes. For that you will need to call C<guestfs_lvs>."); - ("pvs", (RStringList "physvols", []), 9, [], + ("pvs", (RStringList "physvols", []), 9, [Optional "lvm2"], [InitBasicFSonLVM, Always, TestOutputListOfDevices ( [["pvs"]], ["/dev/sda1"]); InitEmpty, Always, TestOutputListOfDevices ( @@ -997,7 +998,7 @@ PVs (eg. C</dev/sda2>). See also C<guestfs_pvs_full>."); - ("vgs", (RStringList "volgroups", []), 10, [], + ("vgs", (RStringList "volgroups", []), 10, [Optional "lvm2"], [InitBasicFSonLVM, Always, TestOutputList ( [["vgs"]], ["VG"]); InitEmpty, Always, TestOutputList ( @@ -1018,7 +1019,7 @@ detected (eg. C<VolGroup00>). See also C<guestfs_vgs_full>."); - ("lvs", (RStringList "logvols", []), 11, [], + ("lvs", (RStringList "logvols", []), 11, [Optional "lvm2"], [InitBasicFSonLVM, Always, TestOutputList ( [["lvs"]], ["/dev/VG/LV"]); InitEmpty, Always, TestOutputList ( @@ -1042,21 +1043,21 @@ This returns a list of the logical volume device names See also C<guestfs_lvs_full>."); - ("pvs_full", (RStructList ("physvols", "lvm_pv"), []), 12, [], + ("pvs_full", (RStructList ("physvols", "lvm_pv"), []), 12, [Optional "lvm2"], [], (* XXX how to test? *) "list the LVM physical volumes (PVs)", "\ List all the physical volumes detected. This is the equivalent of the L<pvs(8)> command. The \"full\" version includes all fields."); - ("vgs_full", (RStructList ("volgroups", "lvm_vg"), []), 13, [], + ("vgs_full", (RStructList ("volgroups", "lvm_vg"), []), 13, [Optional "lvm2"], [], (* XXX how to test? *) "list the LVM volume groups (VGs)", "\ List all the volumes groups detected. This is the equivalent of the L<vgs(8)> command. The \"full\" version includes all fields."); - ("lvs_full", (RStructList ("logvols", "lvm_lv"), []), 14, [], + ("lvs_full", (RStructList ("logvols", "lvm_lv"), []), 14, [Optional "lvm2"], [], (* XXX how to test? *) "list the LVM logical volumes (LVs)", "\ @@ -1080,7 +1081,7 @@ Note that this function cannot correctly handle binary files as end of line). For those you need to use the C<guestfs_read_file> function which has a more complex interface."); - ("aug_init", (RErr, [Pathname "root"; Int "flags"]), 16, [], + ("aug_init", (RErr, [Pathname "root"; Int "flags"]), 16, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "create a new Augeas handle", "\ @@ -1131,7 +1132,7 @@ To close the handle, you can call C<guestfs_aug_close>. To find out more about Augeas, see L<http://augeas.net/>."); - ("aug_close", (RErr, []), 26, [], + ("aug_close", (RErr, []), 26, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "close the current Augeas handle", "\ @@ -1140,7 +1141,7 @@ 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", [String "name"; OptString "expr"]), 17, [], + ("aug_defvar", (RInt "nrnodes", [String "name"; OptString "expr"]), 17, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "define an Augeas variable", "\ @@ -1151,7 +1152,7 @@ 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", (RStruct ("nrnodescreated", "int_bool"), [String "name"; String "expr"; String "val"]), 18, [], + ("aug_defnode", (RStruct ("nrnodescreated", "int_bool"), [String "name"; String "expr"; String "val"]), 18, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "define an Augeas node", "\ @@ -1166,20 +1167,20 @@ 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", [String "augpath"]), 19, [], + ("aug_get", (RString "val", [String "augpath"]), 19, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "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", (RErr, [String "augpath"; String "val"]), 20, [], + ("aug_set", (RErr, [String "augpath"; String "val"]), 20, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "set Augeas path to value", "\ Set the value associated with C<path> to C<value>."); - ("aug_insert", (RErr, [String "augpath"; String "label"; Bool "before"]), 21, [], + ("aug_insert", (RErr, [String "augpath"; String "label"; Bool "before"]), 21, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "insert a sibling Augeas node", "\ @@ -1191,7 +1192,7 @@ 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", [String "augpath"]), 22, [], + ("aug_rm", (RInt "nrnodes", [String "augpath"]), 22, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "remove an Augeas path", "\ @@ -1199,14 +1200,14 @@ Remove C<path> and all of its children. On success this returns the number of entries which were removed."); - ("aug_mv", (RErr, [String "src"; String "dest"]), 23, [], + ("aug_mv", (RErr, [String "src"; String "dest"]), 23, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "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", [String "augpath"]), 24, [], + ("aug_match", (RStringList "matches", [String "augpath"]), 24, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "return Augeas nodes which match augpath", "\ @@ -1214,7 +1215,7 @@ 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", (RErr, []), 25, [], + ("aug_save", (RErr, []), 25, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "write all pending Augeas changes to disk", "\ @@ -1223,7 +1224,7 @@ 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", (RErr, []), 27, [], + ("aug_load", (RErr, []), 27, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "load files into the tree", "\ @@ -1232,7 +1233,7 @@ Load files into the tree. See C<aug_load> in the Augeas documentation for the full gory details."); - ("aug_ls", (RStringList "matches", [String "augpath"]), 28, [], + ("aug_ls", (RStringList "matches", [String "augpath"]), 28, [Optional "augeas"], [], (* XXX Augeas code needs tests. *) "list Augeas nodes under augpath", "\ @@ -1365,7 +1366,7 @@ other objects like files. See also C<guestfs_stat>."); - ("pvcreate", (RErr, [Device "device"]), 39, [], + ("pvcreate", (RErr, [Device "device"]), 39, [Optional "lvm2"], [InitEmpty, Always, TestOutputListOfDevices ( [["sfdiskM"; "/dev/sda"; ",100 ,200 ,"]; ["pvcreate"; "/dev/sda1"]; @@ -1378,7 +1379,7 @@ This creates an LVM physical volume on the named C<device>, where C<device> should usually be a partition name such as C</dev/sda1>."); - ("vgcreate", (RErr, [String "volgroup"; DeviceList "physvols"]), 40, [], + ("vgcreate", (RErr, [String "volgroup"; DeviceList "physvols"]), 40, [Optional "lvm2"], [InitEmpty, Always, TestOutputList ( [["sfdiskM"; "/dev/sda"; ",100 ,200 ,"]; ["pvcreate"; "/dev/sda1"]; @@ -1392,7 +1393,7 @@ as C</dev/sda1>."); This creates an LVM volume group called C<volgroup> from the non-empty list of physical volumes C<physvols>."); - ("lvcreate", (RErr, [String "logvol"; String "volgroup"; Int "mbytes"]), 41, [], + ("lvcreate", (RErr, [String "logvol"; String "volgroup"; Int "mbytes"]), 41, [Optional "lvm2"], [InitEmpty, Always, TestOutputList ( [["sfdiskM"; "/dev/sda"; ",100 ,200 ,"]; ["pvcreate"; "/dev/sda1"]; @@ -1543,7 +1544,7 @@ This unmounts all mounted filesystems. Some internal mounts are not unmounted by this call."); - ("lvm_remove_all", (RErr, []), 48, [DangerWillRobinson], + ("lvm_remove_all", (RErr, []), 48, [DangerWillRobinson; Optional "lvm2"], [], "remove all LVM LVs, VGs and PVs", "\ @@ -2034,7 +2035,7 @@ There is no comprehensive help for this command. You have to look at the file C<daemon/debug.c> in the libguestfs source to find out what you can do."); - ("lvremove", (RErr, [Device "device"]), 77, [], + ("lvremove", (RErr, [Device "device"]), 77, [Optional "lvm2"], [InitEmpty, Always, TestOutputList ( [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; @@ -2067,7 +2068,7 @@ the path to the LV, such as C</dev/VG/LV>. You can also remove all LVs in a volume group by specifying the VG name, C</dev/VG>."); - ("vgremove", (RErr, [String "vgname"]), 78, [], + ("vgremove", (RErr, [String "vgname"]), 78, [Optional "lvm2"], [InitEmpty, Always, TestOutputList ( [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; @@ -2091,7 +2092,7 @@ Remove an LVM volume group C<vgname>, (for example C<VG>). This also forcibly removes all logical volumes in the volume group (if any)."); - ("pvremove", (RErr, [Device "device"]), 79, [], + ("pvremove", (RErr, [Device "device"]), 79, [Optional "lvm2"], [InitEmpty, Always, TestOutputListOfDevices ( [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; @@ -2386,7 +2387,7 @@ The returned strings are transcoded to UTF-8."); This runs C<hexdump -C> on the given C<path>. The result is the human-readable, canonical hex dump of the file."); - ("zerofree", (RErr, [Device "device"]), 97, [], + ("zerofree", (RErr, [Device "device"]), 97, [Optional "zerofree"], [InitNone, Always, TestOutput ( [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext3"; "/dev/sda1"]; @@ -2409,7 +2410,7 @@ mounted. It is possible that using this program can damage the filesystem or data on the filesystem."); - ("pvresize", (RErr, [Device "device"]), 98, [], + ("pvresize", (RErr, [Device "device"]), 98, [Optional "lvm2"], [], "resize an LVM physical volume", "\ @@ -2461,7 +2462,7 @@ kernel's idea of the geometry (see C<guestfs_sfdisk_kernel_geometry>). The result is in human-readable format, and not designed to be parsed."); - ("vg_activate_all", (RErr, [Bool "activate"]), 103, [], + ("vg_activate_all", (RErr, [Bool "activate"]), 103, [Optional "lvm2"], [], "activate or deactivate all volume groups", "\ @@ -2473,7 +2474,7 @@ then those devices disappear. This command is the same as running C<vgchange -a y|n>"); - ("vg_activate", (RErr, [Bool "activate"; StringList "volgroups"]), 104, [], + ("vg_activate", (RErr, [Bool "activate"; StringList "volgroups"]), 104, [Optional "lvm2"], [], "activate or deactivate some volume groups", "\ @@ -2488,7 +2489,7 @@ This command is the same as running C<vgchange -a y|n volgroups...> Note that if C<volgroups> is an empty list then B<all> volume groups are activated or deactivated."); - ("lvresize", (RErr, [Device "device"; Int "mbytes"]), 105, [], + ("lvresize", (RErr, [Device "device"; Int "mbytes"]), 105, [Optional "lvm2"], [InitNone, Always, TestOutput ( [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; @@ -2581,7 +2582,7 @@ This command is only needed because of C<guestfs_resize2fs> "\ Sleep for C<secs> seconds."); - ("ntfs_3g_probe", (RInt "status", [Bool "rw"; Device "device"]), 110, [], + ("ntfs_3g_probe", (RInt "status", [Bool "rw"; Device "device"]), 110, [Optional "ntfs3g"], [InitNone, Always, TestOutputInt ( [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ntfs"; "/dev/sda1"]; @@ -2663,7 +2664,7 @@ It is just a wrapper around the C L<glob(3)> function with flags C<GLOB_MARK|GLOB_BRACE>. See that manual page for more details."); - ("scrub_device", (RErr, [Device "device"]), 114, [DangerWillRobinson], + ("scrub_device", (RErr, [Device "device"]), 114, [DangerWillRobinson; Optional "scrub"], [InitNone, Always, TestRun ( (* use /dev/sdc because it's smaller *) [["scrub_device"; "/dev/sdc"]])], "scrub (securely wipe) a device", @@ -2674,7 +2675,7 @@ more difficult. It is an interface to the L<scrub(1)> program. See that manual page for more details."); - ("scrub_file", (RErr, [Pathname "file"]), 115, [], + ("scrub_file", (RErr, [Pathname "file"]), 115, [Optional "scrub"], [InitBasicFS, Always, TestRun ( [["write_file"; "/file"; "content"; "0"]; ["scrub_file"; "/file"]])], @@ -2688,7 +2689,7 @@ The file is I<removed> after scrubbing. It is an interface to the L<scrub(1)> program. See that manual page for more details."); - ("scrub_freespace", (RErr, [Pathname "dir"]), 116, [], + ("scrub_freespace", (RErr, [Pathname "dir"]), 116, [Optional "scrub"], [], (* XXX needs testing *) "scrub (securely wipe) free space", "\ @@ -2892,7 +2893,7 @@ a limitation of the kernel or swap tools."); "\ Create a swap partition on C<device> with UUID C<uuid>."); - ("mknod", (RErr, [Int "mode"; Int "devmajor"; Int "devminor"; Pathname "path"]), 133, [], + ("mknod", (RErr, [Int "mode"; Int "devmajor"; Int "devminor"; Pathname "path"]), 133, [Optional "mknod"], [InitBasicFS, Always, TestOutputStruct ( [["mknod"; "0o10777"; "0"; "0"; "/node"]; (* NB: default umask 022 means 0777 -> 0755 in these tests *) @@ -2910,7 +2911,7 @@ constants. C<devmajor> and C<devminor> are the device major and minor numbers, only used when creating block and character special devices."); - ("mkfifo", (RErr, [Int "mode"; Pathname "path"]), 134, [], + ("mkfifo", (RErr, [Int "mode"; Pathname "path"]), 134, [Optional "mknod"], [InitBasicFS, Always, TestOutputStruct ( [["mkfifo"; "0o777"; "/node"]; ["stat"; "/node"]], [CompareWithInt ("mode", 0o10755)])], @@ -2920,7 +2921,7 @@ This call creates a FIFO (named pipe) called C<path> with mode C<mode>. It is just a convenient wrapper around C<guestfs_mknod>."); - ("mknod_b", (RErr, [Int "mode"; Int "devmajor"; Int "devminor"; Pathname "path"]), 135, [], + ("mknod_b", (RErr, [Int "mode"; Int "devmajor"; Int "devminor"; Pathname "path"]), 135, [Optional "mknod"], [InitBasicFS, Always, TestOutputStruct ( [["mknod_b"; "0o777"; "99"; "66"; "/node"]; ["stat"; "/node"]], [CompareWithInt ("mode", 0o60755)])], @@ -2930,7 +2931,7 @@ This call creates a block device node called C<path> with mode C<mode> and device major/minor C<devmajor> and C<devminor>. It is just a convenient wrapper around C<guestfs_mknod>."); - ("mknod_c", (RErr, [Int "mode"; Int "devmajor"; Int "devminor"; Pathname "path"]), 136, [], + ("mknod_c", (RErr, [Int "mode"; Int "devmajor"; Int "devminor"; Pathname "path"]), 136, [Optional "mknod"], [InitBasicFS, Always, TestOutputStruct ( [["mknod_c"; "0o777"; "99"; "66"; "/node"]; ["stat"; "/node"]], [CompareWithInt ("mode", 0o20755)])], @@ -3045,7 +3046,7 @@ C<method> must be one of C<gzip>, C<compress> or C<bzip2>. Since 1.0.63, use C<guestfs_file> instead which can now process compressed files."); - ("getxattrs", (RStructList ("xattrs", "xattr"), [Pathname "path"]), 141, [], + ("getxattrs", (RStructList ("xattrs", "xattr"), [Pathname "path"]), 141, [Optional "linuxxattrs"], [], "list extended attributes of a file or directory", "\ @@ -3057,7 +3058,7 @@ L<listxattr(2)> and L<getxattr(2)> calls. See also: C<guestfs_lgetxattrs>, L<attr(5)>."); - ("lgetxattrs", (RStructList ("xattrs", "xattr"), [Pathname "path"]), 142, [], + ("lgetxattrs", (RStructList ("xattrs", "xattr"), [Pathname "path"]), 142, [Optional "linuxxattrs"], [], "list extended attributes of a file or directory", "\ @@ -3067,7 +3068,7 @@ of the link itself."); ("setxattr", (RErr, [String "xattr"; String "val"; Int "vallen"; (* will be BufferIn *) - Pathname "path"]), 143, [], + Pathname "path"]), 143, [Optional "linuxxattrs"], [], "set extended attribute of a file or directory", "\ @@ -3079,7 +3080,7 @@ See also: C<guestfs_lsetxattr>, L<attr(5)>."); ("lsetxattr", (RErr, [String "xattr"; String "val"; Int "vallen"; (* will be BufferIn *) - Pathname "path"]), 144, [], + Pathname "path"]), 144, [Optional "linuxxattrs"], [], "set extended attribute of a file or directory", "\ @@ -3087,7 +3088,7 @@ This is the same as C<guestfs_setxattr>, but if C<path> is a symbolic link, then it sets an extended attribute of the link itself."); - ("removexattr", (RErr, [String "xattr"; Pathname "path"]), 145, [], + ("removexattr", (RErr, [String "xattr"; Pathname "path"]), 145, [Optional "linuxxattrs"], [], "remove extended attribute of a file or directory", "\ @@ -3096,7 +3097,7 @@ of the file C<path>. See also: C<guestfs_lremovexattr>, L<attr(5)>."); - ("lremovexattr", (RErr, [String "xattr"; Pathname "path"]), 146, [], + ("lremovexattr", (RErr, [String "xattr"; Pathname "path"]), 146, [Optional "linuxxattrs"], [], "remove extended attribute of a file or directory", "\ @@ -3425,7 +3426,7 @@ Create a swap file. This command just writes a swap file signature to an existing file. To create the file itself, use something like C<guestfs_fallocate>."); - ("inotify_init", (RErr, [Int "maxevents"]), 179, [], + ("inotify_init", (RErr, [Int "maxevents"]), 179, [Optional "inotify"], [InitISOFS, Always, TestRun ( [["inotify_init"; "0"]])], "create an inotify handle", @@ -3466,7 +3467,7 @@ as exposed by the Linux kernel, which is roughly what we expose via libguestfs. Note that there is one global inotify handle per libguestfs instance."); - ("inotify_add_watch", (RInt64 "wd", [Pathname "path"; Int "mask"]), 180, [], + ("inotify_add_watch", (RInt64 "wd", [Pathname "path"; Int "mask"]), 180, [Optional "inotify"], [InitBasicFS, Always, TestOutputList ( [["inotify_init"; "0"]; ["inotify_add_watch"; "/"; "1073741823"]; @@ -3485,14 +3486,14 @@ Note for non-C or non-Linux callers: the inotify events are defined by the Linux kernel ABI and are listed in C</usr/include/sys/inotify.h>."); - ("inotify_rm_watch", (RErr, [Int(*XXX64*) "wd"]), 181, [], + ("inotify_rm_watch", (RErr, [Int(*XXX64*) "wd"]), 181, [Optional "inotify"], [], "remove an inotify watch", "\ Remove a previously defined inotify watch. See C<guestfs_inotify_add_watch>."); - ("inotify_read", (RStructList ("events", "inotify_event"), []), 182, [], + ("inotify_read", (RStructList ("events", "inotify_event"), []), 182, [Optional "inotify"], [], "return list of inotify events", "\ @@ -3507,7 +3508,7 @@ returns an empty list. The reason is that the call will read events up to the maximum appliance-to-host message size and leave remaining events in the queue."); - ("inotify_files", (RStringList "paths", []), 183, [], + ("inotify_files", (RStringList "paths", []), 183, [Optional "inotify"], [], "return list of watched files that had events", "\ @@ -3515,7 +3516,7 @@ This function is a helpful wrapper around C<guestfs_inotify_read> which just returns a list of pathnames of objects that were touched. The returned pathnames are sorted and deduplicated."); - ("inotify_close", (RErr, []), 184, [], + ("inotify_close", (RErr, []), 184, [Optional "inotify"], [], "close the inotify handle", "\ @@ -3523,7 +3524,7 @@ This closes the inotify handle which was previously opened by inotify_init. It removes all watches, throws away any pending events, and deallocates all resources."); - ("setcon", (RErr, [String "context"]), 185, [], + ("setcon", (RErr, [String "context"]), 185, [Optional "selinux"], [], "set SELinux security context", "\ @@ -3532,7 +3533,7 @@ to the string C<context>. See the documentation about SELINUX in L<guestfs(3)>."); - ("getcon", (RString "context", []), 186, [], + ("getcon", (RString "context", []), 186, [Optional "selinux"], [], "get SELinux security context", "\ @@ -3625,7 +3626,7 @@ an external journal on the journal with UUID C<uuid>. See also C<guestfs_mke2journal_U>."); - ("modprobe", (RErr, [String "modulename"]), 194, [], + ("modprobe", (RErr, [String "modulename"]), 194, [Optional "linuxmodules"], [InitNone, Always, TestRun [["modprobe"; "fat"]]], "load a kernel module", "\ @@ -3837,7 +3838,7 @@ might cause the protocol message size to be exceeded, causing this call to fail. The caller must split up such requests into smaller groups of names."); - ("lxattrlist", (RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"]), 205, [], + ("lxattrlist", (RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"]), 205, [Optional "linuxxattrs"], [], (* XXX *) "lgetxattr on multiple files", "\ @@ -4114,8 +4115,8 @@ The precise libguestfs function groups that may be checked by this command are listed in L<guestfs(3)/AVAILABILITY>. The argument C<groups> is a list of API group names, eg: -C<[\"inotify\", \"part\"]> would check for the availability of -the C<guestfs_inotify_*> functions and C<guestfs_part_*> +C<[\"inotify\", \"augeas\"]> would check for the availability of +the C<guestfs_inotify_*> functions and C<guestfs_aug_*> (partition editing) functions. The command returns no error if I<all> requested groups are available. @@ -4583,6 +4584,23 @@ with correct use of these functions." alt in with Not_found -> None +(* Create list of optional groups. *) +let optgroups + let h = Hashtbl.create 13 in + List.iter ( + fun (name, _, _, flags, _, _, _) -> + List.iter ( + function + | Optional group -> + let names = try Hashtbl.find h group with Not_found -> [] in + Hashtbl.replace h group (name :: names) + | _ -> () + ) flags + ) daemon_functions; + let groups = Hashtbl.fold (fun k _ ks -> k :: ks) h [] in + let groups = List.map (fun group -> group, Hashtbl.find h group) groups in + List.sort (fun x y -> compare (fst x) (fst y)) groups + (* Check function names etc. for consistency. *) let check_functions () let contains_uppercase str @@ -4906,6 +4924,21 @@ and generate_structs_pod () pr "\n" ) structs +and generate_availability_pod () + (* Availability documentation. *) + pr "=over 4\n"; + pr "\n"; + List.iter ( + fun (group, functions) -> + pr "=item B<%s>\n" group; + pr "\n"; + pr "The following functions:\n"; + List.iter (pr "L</guestfs_%s>\n") functions; + pr "\n" + ) optgroups; + pr "=back\n"; + pr "\n" + (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'. * @@ -5921,6 +5954,34 @@ and generate_daemon_names () ) daemon_functions; pr "};\n"; +(* Generate the optional groups for the daemon to implement + * guestfs_available. + *) +and generate_daemon_optgroups_c () + generate_header CStyle GPLv2; + + pr "#include <config.h>\n"; + pr "\n"; + pr "#include \"daemon.h\"\n"; + pr "#include \"optgroups.h\"\n"; + pr "\n"; + + pr "struct optgroup optgroups[] = {\n"; + List.iter ( + fun (group, _) -> + pr " { \"%s\", optgroup_%s_available },\n" group group + ) optgroups; + pr " { NULL, NULL }\n"; + pr "};\n" + +and generate_daemon_optgroups_h () + generate_header CStyle GPLv2; + + List.iter ( + fun (group, _) -> + pr "extern int optgroup_%s_available (void);\n" group + ) optgroups + (* Generate the tests. *) and generate_tests () generate_header CStyle GPLv2; @@ -10289,6 +10350,14 @@ Run it from the top source directory using the command generate_daemon_names (); close (); + let close = output_to "daemon/optgroups.c" in + generate_daemon_optgroups_c (); + close (); + + let close = output_to "daemon/optgroups.h" in + generate_daemon_optgroups_h (); + close (); + let close = output_to "capitests/tests.c" in generate_tests (); close (); @@ -10313,6 +10382,10 @@ Run it from the top source directory using the command generate_actions_pod (); close (); + let close = output_to "guestfs-availability.pod" in + generate_availability_pod (); + close (); + let close = output_to "guestfish-actions.pod" in generate_fish_actions_pod (); close (); -- 1.6.5.2
Possibly Parallel Threads
- [PATCH 0/13 v2] Prepare for adding write support to hivex (Windows registry) library
- [PATCH 0/2] Fix doubling of command names in some error messages
- [PATCH 0/7] Prepare for adding write support to hivex (windows registry) library
- [PATCH 0/2] Add readonly=on option to qemu -drive command line
- [PATCH 0/2] /dev/mapper paths should not be returned from C inspection APIs