[PATCH 0/2 NOT WORKING] Symbol versioning
by Richard W.M. Jones
John,
This was my attempt to add symbol versioning to the library,
letting us break ABI without breaking any existing callers.
Unfortunately it doesn't work:
- the new versioned symbols are marked local in libguestfs.so
- the existing symbols should now have @GUESTFS_0.0 versions,
but don't
The documentation for this stuff is extremely thin, and I've
got a bad case of influenza at the moment.
Rich.
12 years
building resize fails due to lack of Unix module
by Olaf Hering
Building 1.9.53 fails for me with ocaml 3.10.2 and 3.12.1, likely due to
commit a0722c7ad846960be54978a31ebe73b76e119203:
[ 318s] ocamlfind ocamlopt -g -warn-error CDEFLMPSUVYZX -package str -I ../src/.libs -I ../ocaml \
[ 318s] mlguestfs.cmxa -linkpkg ../fish/guestfish-progress.o progress-c.o resize_gettext.cmx resize_utils.cmx progress.cmx resize.cmx -cclib -lncurses -o virt-resize
[ 318s] No implementations provided for the following modules:
[ 318s] Unix referenced from progress.cmx
[ 318s] make[2]: *** [virt-resize] Error 2
[ 318s] make[2]: Leaving directory `/usr/src/packages/BUILD/libguestfs-1.19.53/resize'
I'm not sure where Unix is supposed to come from, the Printf files exist:
bax:~> rpm -ql ocaml | grep -iw unix
/usr/lib64/ocaml/unix.a
/usr/lib64/ocaml/unix.cma
/usr/lib64/ocaml/unix.cmi
/usr/lib64/ocaml/unix.cmx
/usr/lib64/ocaml/unix.cmxa
/usr/lib64/ocaml/unix.mli
/usr/lib64/ocaml/vmthreads/unix.cma
bax:~> rpm -ql ocaml | grep -iw printf
/usr/lib64/ocaml/printf.cmi
/usr/lib64/ocaml/printf.cmx
/usr/lib64/ocaml/printf.ml
/usr/lib64/ocaml/printf.mli
/usr/lib64/ocaml/printf.p.cmx
bax:~>
Is the ocaml.rpm built or packaged incorrectly?
Olaf
12 years
[PATCH] windows: Fix creation of /Temp/V2V directory (RHBZ#868073).
by Richard W.M. Jones
From: "Richard W.M. Jones" <rjones(a)redhat.com>
case_sensitive_path was not defined when called on a path where the
final element doesn't exist. In libguestfs >= 1.16.29 it was changed
to return non-NULL if the final element doesn't exist so that creation
of new files works.
---
lib/Sys/VirtConvert/Converter/Windows.pm | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/lib/Sys/VirtConvert/Converter/Windows.pm b/lib/Sys/VirtConvert/Converter/Windows.pm
index ce89a67..d4885be 100644
--- a/lib/Sys/VirtConvert/Converter/Windows.pm
+++ b/lib/Sys/VirtConvert/Converter/Windows.pm
@@ -470,12 +470,8 @@ sub _upload_files
foreach my $d ('Temp', 'V2V') {
$path .= '/'.$d;
- eval { $path = $g->case_sensitive_path($path) };
-
- # case_sensitive_path will fail if the path doesn't exist
- if ($@) {
- $g->mkdir($path);
- }
+ $path = $g->case_sensitive_path($path);
+ $g->mkdir_p($path);
}
foreach my $file (@fb_files) {
--
1.7.11.4
12 years
[PATCH 0/10] Add a mini-library for running external commands.
by Richard W.M. Jones
Inspired by libvirt's virCommand* internal mini-library, this adds
some internal APIs for running commands.
The first patch contains the new APIs. The subsequent patches change
various parts of the library over to use it.
Rich.
12 years
[PATCH] NEW API: mktemp
by Wanlong Gao
Used to create temporary directory or file with an optional suffix.
Signed-off-by: Wanlong Gao <gaowanlong(a)cn.fujitsu.com>
---
daemon/dir.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++
generator/actions.ml | 36 +++++++++++++++++++++++++++++++
gobject/Makefile.inc | 6 ++++--
po/POTFILES | 2 ++
src/MAX_PROC_NR | 2 +-
5 files changed, 104 insertions(+), 3 deletions(-)
diff --git a/daemon/dir.c b/daemon/dir.c
index aed45d6..b58cc2a 100644
--- a/daemon/dir.c
+++ b/daemon/dir.c
@@ -212,3 +212,64 @@ do_mkdtemp (const char *template)
return r;
}
+
+char *
+do_mktemp (const char *template,
+ int dir,
+ const char *suffix)
+{
+ char *dest_name = NULL;
+ size_t suffix_len = 0;
+ char *r;
+ int err;
+ if (!(optargs_bitmask & GUESTFS_MKTEMP_DIR_BITMASK))
+ dir = 0;
+
+ if (optargs_bitmask & GUESTFS_MKTEMP_SUFFIX_BITMASK) {
+ if (suffix) {
+ if (dir) {
+ reply_with_error ("can not support suffix with directory");
+ return NULL;
+ }
+ size_t len = strlen (template);
+ if (!len || template[len - 1] != 'X') {
+ reply_with_error ("template %s must end in X", template);
+ return NULL;
+ }
+ suffix_len = strlen (suffix);
+ dest_name = malloc (len + suffix_len + 1);
+ memcpy (dest_name, template, len);
+ memcpy (dest_name + len, suffix, suffix_len + 1);
+ }
+ }
+
+ if (dest_name == NULL) {
+ dest_name = strdup (template);
+ if (dest_name == NULL) {
+ reply_with_perror ("strdup");
+ return NULL;
+ }
+ }
+
+ CHROOT_IN;
+ if (dir)
+ r = mkdtemp (dest_name);
+ else
+ err = mkstemps (dest_name, suffix_len);
+ CHROOT_OUT;
+
+ if (dir) {
+ if (r == NULL) {
+ reply_with_perror ("%s", dest_name);
+ free (dest_name);
+ }
+ return r;
+ } else {
+ if (err == -1) {
+ reply_with_perror ("%s", dest_name);
+ free (dest_name);
+ return NULL;
+ }
+ return dest_name;
+ }
+}
diff --git a/generator/actions.ml b/generator/actions.ml
index 13e54f3..855b4d1 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -4834,6 +4834,7 @@ manual page for more details." };
name = "mkdtemp";
style = RString "dir", [Pathname "tmpl"], [];
proc_nr = Some 117;
+ deprecated_by = Some "mktemp";
tests = [
InitScratchFS, Always, TestRun (
[["mkdir"; "/mkdtemp"];
@@ -10013,6 +10014,41 @@ This function is used internally when hotplugging drives." };
longdesc = "\
This function is used internally when hotplugging drives." };
+ { defaults with
+ name = "mktemp";
+ style = RString "path", [Pathname "tmpl"], [OBool "dir"; OString "suffix"];
+ proc_nr = Some 373;
+ tests = [
+ InitScratchFS, Always, TestRun (
+ [["mkdir"; "/mktemp"];
+ ["mktemp"; "/mktemp/tmpXXXXXX"; "true"; "NOARG"];
+ ["mktemp"; "/mktemp/tmpXXXXXX"; "false"; "suff"]])
+ ];
+ shortdesc = "create a temporary directory or file";
+ longdesc = "\
+This command creates a temporary directory/file. The
+C<tmpl> parameter should be a full pathname for the
+temporary directory name with the final six characters being
+\"XXXXXX\".
+
+For example: \"/tmp/myprogXXXXXX\" or \"/Temp/myprogXXXXXX\",
+the second one being suitable for Windows filesystems.
+
+The name of the temporary directory/file that was created
+is returned.
+
+The temporary directory/file is created with mode 0700
+and is owned by root.
+
+The caller is responsible for deleting the temporary
+directory/file and its contents after use.
+
+Set C<dir> to \"true\" if you want to crate a directory.
+
+C<suffix> is used to specify a suffix to append the C<tmpl>.
+
+See also: L<mkdtemp(3)> and L<mkstemps(3)>" };
+
]
(* Non-API meta-commands available only in guestfish.
diff --git a/gobject/Makefile.inc b/gobject/Makefile.inc
index 0a168f4..95a4b6b 100644
--- a/gobject/Makefile.inc
+++ b/gobject/Makefile.inc
@@ -81,7 +81,8 @@ guestfs_gobject_headers= \
include/guestfs-gobject/optargs-xfs_admin.h \
include/guestfs-gobject/optargs-hivex_open.h \
include/guestfs-gobject/optargs-xfs_repair.h \
- include/guestfs-gobject/optargs-mke2fs.h
+ include/guestfs-gobject/optargs-mke2fs.h \
+ include/guestfs-gobject/optargs-mktemp.h
guestfs_gobject_sources= \
src/session.c \
@@ -144,4 +145,5 @@ guestfs_gobject_sources= \
src/optargs-xfs_admin.c \
src/optargs-hivex_open.c \
src/optargs-xfs_repair.c \
- src/optargs-mke2fs.c
+ src/optargs-mke2fs.c \
+ src/optargs-mktemp.c
diff --git a/po/POTFILES b/po/POTFILES
index 3705e74..4bdee3e 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -137,6 +137,7 @@ fish/tilde.c
fish/time.c
format/format.c
fuse/guestmount.c
+gobject/docs/guestfs-scan.c
gobject/src/optargs-add_domain.c
gobject/src/optargs-add_drive.c
gobject/src/optargs-btrfs_filesystem_resize.c
@@ -160,6 +161,7 @@ gobject/src/optargs-mke2fs.c
gobject/src/optargs-mkfs.c
gobject/src/optargs-mkfs_btrfs.c
gobject/src/optargs-mkswap.c
+gobject/src/optargs-mktemp.c
gobject/src/optargs-mount_9p.c
gobject/src/optargs-mount_local.c
gobject/src/optargs-ntfsclone_out.c
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index ba30067..a5c3fde 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-372
+373
--
1.7.12.1.401.gb5d156c
12 years
[PATCH] TODO: remove the already implemented yum cache clean feature
by Wanlong Gao
The cleanup of package managers cache is already implemented
by sprep_operation_package_manager_cache.ml, so remove this
TODO item.
Signed-off-by: Wanlong Gao <gaowanlong(a)cn.fujitsu.com>
---
TODO | 1 -
1 file changed, 1 deletion(-)
diff --git a/TODO b/TODO
index d9dd1d3..44fee34 100644
--- a/TODO
+++ b/TODO
@@ -401,7 +401,6 @@ virt-sysprep ideas
Kazuo Moriwaka adds:
- - "yum clean all" (or the equivalent) to remove yum caches
- swap devices (both of block device and file) should be wiped. This may
good for security purpose, and size. I found virt-sparsify can clear
swap partition.
--
1.8.0.rc2.4.g42e55a5
12 years
Proposed libguestfs API for implementing libvirt virConnectOpenAuth
by Richard W.M. Jones
As in the example code below.
static void
do_auth (guestfs_h *g,
void *opaque,
uint64_t event,
int event_handle,
int flags,
const char *buf, size_t buf_len,
const uint64_t *array, size_t array_len)
{
char **creds;
size_t i;
char *prompt;
char *reply;
size_t replylen;
// buf will be the libvirt URI. It is always \0-terminated so
// buf_len can be ignored in this case.
printf ("Authentication required for libvirt connection '%s'\n", buf);
// Ask libguestfs what credentials libvirt is demanding.
creds = guestfs_get_libvirt_requested_credentials (g);
// Now ask the user for answers.
for (i = 0; creds[i] != NULL; ++i) {
if (strcmp (creds[i], "authname") == 0 ||
strcmp (creds[i], "passphrase") == 0) {
prompt = guestfs_get_libvirt_requested_credential_prompt (g, i);
printf ("%s: ", prompt);
free (prompt);
// ...
// Input from user is in 'reply', length 'replylen' (bytes).
guestfs_set_libvirt_requested_credential (g, i, reply, replylen);
}
free (creds[i]);
}
free (creds);
// On return from this function (libguestfs event), the libvirt
// event returns too.
}
// ...
{
guestfs_h *g;
char *creds[] = { "authname", "passphrase", NULL };
g = guestfs_create ();
guestfs_set_libvirt_supported_credentials (g, creds);
guestfs_set_event_callback (g, do_auth,
GUESTFS_EVENT_LIBVIRT_AUTH, 0, NULL);
// An example of a function that would open a libvirt connection:
guestfs_add_domain (g, "dom",
GUESTFS_ADD_DOMAIN_LIBVIRTURI, "qemu:///system",
-1);
}
----------------------------------------------------------------------
By the way, some of the virt tools can open multiple connections to
libvirt, and it is architecturally hard to change this:
https://www.redhat.com/archives/libguestfs/2012-October/msg00065.html
Does libvirt (or policy kit?) provide any mechanism for caching these
credentials within the same process, so that we don't have to provide
the same creds back to libvirt for multiple connections?
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
virt-df lists disk usage of guests without needing to install any
software inside the virtual machine. Supports Linux and Windows.
http://et.redhat.com/~rjones/virt-df/
12 years, 1 month
simplify debugging of guestfsd
by Olaf Hering
Sometimes guestfsd fails to gather info, and the virt-<tool> -v output
is usually not useful to figure out whats going on within the temporary
guest. I see the /init script has support to run guestfsd with a debug
tool, which is currently valgrind.
Granted, valgrind support is a compile time thing. What would be a good
way to optionally run guestfsd with strace or gdb?
Should each tool get a new option, something like 'virt-<tool>
--debug=strace', or should it be like 'XYZ_STRACE=1 virt-<tool>'?
Olaf
12 years, 1 month
[PATCH] New APIs: Model libvirt authentication events through the API.
by Richard W.M. Jones
From: "Richard W.M. Jones" <rjones(a)redhat.com>
This commit models libvirt authentication events through the API,
adding one new event (GUESTFS_EVENT_LIBVIRT_AUTH) and several new
APIs:
guestfs_set_libvirt_supported_credentials
guestfs_get_libvirt_requested_credentials
guestfs_get_libvirt_requested_credential_prompt
guestfs_get_libvirt_requested_credential_challenge
guestfs_get_libvirt_requested_credential_defresult
guestfs_set_libvirt_requested_credential
See the documentation and example which shows how to use the new API.
This commit also changes existing calls to virConnectOpen* within the
library so that the new API is used.
Also included is an example (but not a test, because it's hard to see
how to automatically test the libvirt API).
---
.gitignore | 1 +
examples/Makefile.am | 13 +-
examples/libvirt_auth.c | 167 +++++++++++++++++++
generator/actions.ml | 101 ++++++++++++
generator/events.ml | 2 +
ocaml/t/guestfs_400_events.ml | 3 +-
po/POTFILES | 1 +
src/Makefile.am | 1 +
src/guestfs-internal.h | 15 ++
src/guestfs.pod | 136 ++++++++++++++++
src/launch-libvirt.c | 3 +-
src/libvirt-auth.c | 369 ++++++++++++++++++++++++++++++++++++++++++
src/libvirt-domain.c | 2 +-
13 files changed, 809 insertions(+), 5 deletions(-)
create mode 100644 examples/libvirt_auth.c
create mode 100644 src/libvirt-auth.c
diff --git a/.gitignore b/.gitignore
index 75fa7ab..6a59aec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -99,6 +99,7 @@ Makefile.in
/examples/guestfs-recipes.1
/examples/guestfs-testing.1
/examples/inspect_vm
+/examples/libvirt_auth
/examples/mount_local
/examples/stamp-guestfs-examples.pod
/examples/stamp-guestfs-faq.pod
diff --git a/examples/Makefile.am b/examples/Makefile.am
index a7c9903..bf2db45 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -32,7 +32,7 @@ CLEANFILES = \
noinst_PROGRAMS = create_disk display_icon inspect_vm
if HAVE_LIBVIRT
-noinst_PROGRAMS += copy_over
+noinst_PROGRAMS += copy_over libvirt_auth
endif
if HAVE_HIVEX
noinst_PROGRAMS += virt-dhcp-address
@@ -52,6 +52,17 @@ copy_over_CFLAGS = \
copy_over_LDADD = \
$(top_builddir)/src/libguestfs.la \
$(LIBVIRT_LIBS)
+
+libvirt_auth_SOURCES = libvirt_auth.c
+libvirt_auth_CFLAGS = \
+ -DGUESTFS_WARN_DEPRECATED=1 \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(LIBVIRT_CFLAGS) \
+ -pthread \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+libvirt_auth_LDADD = \
+ $(top_builddir)/src/libguestfs.la \
+ $(LIBVIRT_LIBS)
endif
create_disk_SOURCES = create_disk.c
diff --git a/examples/libvirt_auth.c b/examples/libvirt_auth.c
new file mode 100644
index 0000000..f0b8bbb
--- /dev/null
+++ b/examples/libvirt_auth.c
@@ -0,0 +1,167 @@
+/* Example of using the libvirt authentication event-driven API. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <guestfs.h>
+
+static void
+usage (void)
+{
+ fprintf (stderr,
+ "Usage:\n"
+ "\n"
+ " libvirt_auth URI domain\n"
+ "\n"
+ "where:\n"
+ "\n"
+ " URI is the libvirt URI, eg. qemu+libssh2://USER@localhost/system\n"
+ " domain is the name of the guest\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ " libvirt_auth 'qemu+libssh2://USER@localhost/system' 'foo'\n"
+ "\n"
+ "would connect (read-only) to libvirt URI given and open the guest\n"
+ "called 'foo' and list some information about its filesystems.\n"
+ "\n"
+ "The important point of this example is that any libvirt authentication\n"
+ "required to connect to the server should be done.\n"
+ "\n");
+}
+
+static void auth_callback (guestfs_h *g, void *opaque, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len);
+
+int
+main (int argc, char *argv[])
+{
+ const char *uri, *dom;
+ guestfs_h *g;
+ const char *creds[] = { "authname", "passphrase",
+ "echoprompt", "noechoprompt", NULL };
+ int r, eh;
+ char **filesystems;
+ size_t i;
+
+ if (argc != 3) {
+ usage ();
+ exit (EXIT_FAILURE);
+ }
+ uri = argv[1];
+ dom = argv[2];
+
+ g = guestfs_create ();
+ if (!g)
+ exit (EXIT_FAILURE);
+
+ r = guestfs_set_libvirt_supported_credentials (g, (char **) creds);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ /* Set up the event handler. */
+ eh = guestfs_set_event_callback (g, auth_callback,
+ GUESTFS_EVENT_LIBVIRT_AUTH, 0, NULL);
+ if (eh == -1)
+ exit (EXIT_FAILURE);
+
+ /* Add the named domain. */
+ r = guestfs_add_domain (g, dom,
+ GUESTFS_ADD_DOMAIN_LIBVIRTURI, uri,
+ -1);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ /* Launch and do some simple inspection. */
+ r = guestfs_launch (g);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ filesystems = guestfs_list_filesystems (g);
+
+ for (i = 0; filesystems[i] != NULL; i += 2) {
+ printf ("%s:%s is a %s filesystem\n",
+ dom, filesystems[i], filesystems[i+1]);
+ free (filesystems[i]);
+ free (filesystems[i+1]);
+ }
+ free (filesystems);
+
+ exit (EXIT_SUCCESS);
+}
+
+static void
+auth_callback (guestfs_h *g,
+ void *opaque,
+ uint64_t event,
+ int event_handle,
+ int flags,
+ const char *buf, size_t buf_len,
+ const uint64_t *array, size_t array_len)
+{
+ char **creds;
+ size_t i;
+ char *prompt;
+ char *reply = NULL;
+ size_t replylen;
+ char *pass;
+ ssize_t len;
+ int r;
+
+ printf ("libvirt_auth.c: authentication required for libvirt URI '%s'\n\n",
+ buf);
+
+ /* Ask libguestfs what credentials libvirt is demanding. */
+ creds = guestfs_get_libvirt_requested_credentials (g);
+ if (creds == NULL)
+ exit (EXIT_FAILURE);
+
+ /* Now ask the user for answers. */
+ for (i = 0; creds[i] != NULL; ++i)
+ {
+ printf ("libvirt_auth.c: credential '%s'\n", creds[i]);
+
+ if (strcmp (creds[i], "authname") == 0 ||
+ strcmp (creds[i], "echoprompt") == 0) {
+ prompt = guestfs_get_libvirt_requested_credential_prompt (g, i);
+ if (prompt && strcmp (prompt, "") != 0)
+ printf ("%s: ", prompt);
+ free (prompt);
+
+ len = getline (&reply, &replylen, stdin);
+ if (len == -1) {
+ perror ("getline");
+ exit (EXIT_FAILURE);
+ }
+ if (len > 0 && reply[len-1] == '\n')
+ reply[--len] = '\0';
+
+ r = guestfs_set_libvirt_requested_credential (g, i, reply, len);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+ } else if (strcmp (creds[i], "passphrase") == 0 ||
+ strcmp (creds[i], "noechoprompt") == 0) {
+ prompt = guestfs_get_libvirt_requested_credential_prompt (g, i);
+ if (prompt && strcmp (prompt, "") != 0)
+ printf ("%s: ", prompt);
+ free (prompt);
+
+ pass = getpass ("");
+ if (pass == NULL) {
+ perror ("getpass");
+ exit (EXIT_FAILURE);
+ }
+ len = strlen (pass);
+
+ r = guestfs_set_libvirt_requested_credential (g, i, pass, len);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+ }
+
+ free (creds[i]);
+ }
+
+ free (reply);
+ free (creds);
+}
diff --git a/generator/actions.ml b/generator/actions.ml
index 13e54f3..ac8e354 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -2379,6 +2379,107 @@ unplug the drive: see L<guestfs(3)/HOTPLUGGING>. The disk B<must not>
be in use (eg. mounted) when you do this. We try to detect if the
disk is in use and stop you from doing this." };
+ { defaults with
+ name = "set_libvirt_supported_credentials";
+ style = RErr, [StringList "creds"], [];
+ tests = [];
+ shortdesc = "set libvirt credentials supported by calling program";
+ longdesc = "\
+Call this function before setting an event handler for
+C<GUESTFS_EVENT_LIBVIRT_AUTH>, to supply the list of credential types
+that the program knows how to process.
+
+The C<creds> list must be a non-empty list of strings.
+Possible strings are:
+
+=over 4
+
+=item C<username>
+
+=item C<authname>
+
+=item C<language>
+
+=item C<cnonce>
+
+=item C<passphrase>
+
+=item C<echoprompt>
+
+=item C<noechoprompt>
+
+=item C<realm>
+
+=item C<external>
+
+=back
+
+See libvirt documentation for the meaning of these credential types.
+
+See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
+
+ { defaults with
+ name = "get_libvirt_requested_credentials";
+ style = RStringList "creds", [], [];
+ tests = [];
+ shortdesc = "get list of credentials requested by libvirt";
+ longdesc = "\
+This should only be called during the event callback
+for events of type C<GUESTFS_EVENT_LIBVIRT_AUTH>.
+
+Return the list of credentials requested by libvirt. Possible
+values are a subset of the strings provided when you called
+C<guestfs_set_libvirt_supported_credentials>.
+
+See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
+
+ { defaults with
+ name = "get_libvirt_requested_credential_prompt";
+ style = RString "prompt", [Int "index"], [];
+ tests = [];
+ shortdesc = "prompt of i'th requested credential";
+ longdesc = "\
+Get the prompt (provided by libvirt) for the C<index>'th
+requested credential. If libvirt did not provide a prompt,
+this returns the empty string C<\"\">.
+
+See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
+
+ { defaults with
+ name = "get_libvirt_requested_credential_challenge";
+ style = RString "challenge", [Int "index"], [];
+ tests = [];
+ shortdesc = "challenge of i'th requested credential";
+ longdesc = "\
+Get the challenge (provided by libvirt) for the C<index>'th
+requested credential. If libvirt did not provide a challenge,
+this returns the empty string C<\"\">.
+
+See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
+
+ { defaults with
+ name = "get_libvirt_requested_credential_defresult";
+ style = RString "defresult", [Int "index"], [];
+ tests = [];
+ shortdesc = "default result of i'th requested credential";
+ longdesc = "\
+Get the default result (provided by libvirt) for the C<index>'th
+requested credential. If libvirt did not provide a default result,
+this returns the empty string C<\"\">.
+
+See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
+
+ { defaults with
+ name = "set_libvirt_requested_credential";
+ style = RErr, [Int "index"; BufferIn "cred"], [];
+ tests = [];
+ shortdesc = "pass requested credential back to libvirt";
+ longdesc = "\
+After requesting the C<index>'th credential from the user,
+call this function to pass the answer back to libvirt.
+
+See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
+
]
(* daemon_functions are any functions which cause some action
diff --git a/generator/events.ml b/generator/events.ml
index d025f57..1062302 100644
--- a/generator/events.ml
+++ b/generator/events.ml
@@ -37,6 +37,8 @@ let events = [
"trace"; (* call trace messages *)
"enter"; (* enter a function *)
+
+ "libvirt_auth"; (* libvirt authentication request *)
]
let events = mapi (fun i name -> name, 1 lsl i) events
diff --git a/ocaml/t/guestfs_400_events.ml b/ocaml/t/guestfs_400_events.ml
index 4585a09..be40608 100644
--- a/ocaml/t/guestfs_400_events.ml
+++ b/ocaml/t/guestfs_400_events.ml
@@ -28,7 +28,8 @@ let log g ev eh buf array =
| Guestfs.EVENT_APPLIANCE -> "appliance"
| Guestfs.EVENT_LIBRARY -> "library"
| Guestfs.EVENT_TRACE -> "trace"
- | Guestfs.EVENT_ENTER -> "enter" in
+ | Guestfs.EVENT_ENTER -> "enter"
+ | Guestfs.EVENT_LIBVIRT_AUTH -> "libvirt_auth" in
let eh : int = Obj.magic eh in
diff --git a/po/POTFILES b/po/POTFILES
index 3705e74..815b541 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -236,6 +236,7 @@ src/launch-appliance.c
src/launch-libvirt.c
src/launch-unix.c
src/launch.c
+src/libvirt-auth.c
src/libvirt-domain.c
src/listfs.c
src/match.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 9b57716..b189c4a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -142,6 +142,7 @@ libguestfs_la_SOURCES = \
launch-appliance.c \
launch-libvirt.c \
launch-unix.c \
+ libvirt-auth.c \
libvirt-domain.c \
listfs.c \
match.c \
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 784f762..8090613 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -293,6 +293,16 @@ struct guestfs_h
int ml_debug_calls; /* Extra debug info on each FUSE call. */
#endif
+#ifdef HAVE_LIBVIRT
+ /* Used by src/libvirt-auth.c. */
+#define NR_CREDENTIAL_TYPES 9
+ unsigned int nr_supported_credentials;
+ int supported_credentials[NR_CREDENTIAL_TYPES];
+ const char *saved_libvirt_uri; /* Doesn't need to be freed. */
+ unsigned int nr_requested_credentials;
+ virConnectCredentialPtr requested_credentials;
+#endif
+
/**** Private data for attach-methods. ****/
/* NB: This cannot be a union because of a pathological case where
* the user changes attach-method while reusing the handle to launch
@@ -567,4 +577,9 @@ extern int guestfs___read_db_dump (guestfs_h *g, const char *dumpfile, void *opa
extern void guestfs___free_fuse (guestfs_h *g);
#endif
+/* libvirt-auth.c */
+#ifdef HAVE_LIBVIRT
+extern virConnectPtr guestfs___open_libvirt_connection (guestfs_h *g, const char *uri, unsigned int flags);
+#endif
+
#endif /* GUESTFS_INTERNAL_H_ */
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 151c7ad..7002f46 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -2232,6 +2232,16 @@ do not generate this event.
If no callback is registered: the event is ignored.
+=item GUESTFS_EVENT_LIBVIRT_AUTH
+(payload type: libvirt URI)
+
+For any API function that opens a libvirt connection, this
+event may be generated to indicate that libvirt demands
+authentication information. See L</LIBVIRT AUTHENTICATION> below.
+
+If no callback is registered: C<virConnectAuthPtrDefault> is
+used (suitable for command-line programs only).
+
=back
=head2 EVENT API
@@ -2351,6 +2361,132 @@ this example, messages are directed to syslog:
syslog (priority, "event 0x%lx: %s", event, buf);
}
+=head2 LIBVIRT AUTHENTICATION
+
+Some libguestfs API calls can open libvirt connections. Currently the
+only ones are L</guestfs_add_domain>; and L</guestfs_launch> if the
+libvirt attach-method has been selected. Libvirt connections may
+require authentication, for example if they need to access a remote
+server or to access root services from non-root. Libvirt
+authentication happens via a callback mechanism, see
+L<http://libvirt.org/guide/html/Application_Development_Guide-Connections.html>
+
+You may provide libvirt authentication data by registering a callback
+for events of type C<GUESTFS_EVENT_LIBVIRT_AUTH>.
+
+If no such event is registered, then libguestfs uses a libvirt
+function that provides command-line prompts
+(C<virConnectAuthPtrDefault>). This is only suitable for command-line
+libguestfs programs.
+
+To provide authentication, first call
+L</guestfs_set_libvirt_supported_credentials> with the list of
+credentials your program knows how to provide. Second, register a
+callback for the C<GUESTFS_EVENT_LIBVIRT_AUTH> event. The event
+handler will be called when libvirt is requesting authentication
+information.
+
+In the event handler, call
+L</guestfs_get_libvirt_requested_credentials> to get a list of the
+credentials that libvirt is asking for. You then need to ask (eg. the
+user) for each credential, and call
+L</guestfs_set_libvirt_requested_credential> with the answer. Note
+that for each credential, additional information may be available
+via the calls
+L</guestfs_get_libvirt_requested_credential_prompt>,
+L</guestfs_get_libvirt_requested_credential_challenge> or
+L</guestfs_get_libvirt_requested_credential_defresult>.
+
+The example program below should make this clearer.
+
+There is also a more substantial working example program supplied with
+the libguestfs sources, called C<libvirt_auth.c>.
+
+ main ()
+ {
+ guestfs_h *g;
+ char *creds[] = { "authname", "passphrase", NULL };
+ int r, eh;
+
+ g = guestfs_create ();
+ if (!g) exit (EXIT_FAILURE);
+
+ /* Tell libvirt what credentials the program supports. */
+ r = guestfs_set_libvirt_supported_credentials (g, creds);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ /* Set up the event handler. */
+ eh = guestfs_set_event_callback (
+ g, do_auth,
+ GUESTFS_EVENT_LIBVIRT_AUTH, 0, NULL);
+ if (eh == -1)
+ exit (EXIT_FAILURE);
+
+ /* An example of a call that may ask for credentials. */
+ r = guestfs_add_domain (
+ g, "dom",
+ GUESTFS_ADD_DOMAIN_LIBVIRTURI, "qemu:///system",
+ -1);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ exit (EXIT_SUCCESS);
+ }
+
+ static void
+ do_auth (guestfs_h *g,
+ void *opaque,
+ uint64_t event,
+ int event_handle,
+ int flags,
+ const char *buf, size_t buf_len,
+ const uint64_t *array, size_t array_len)
+ {
+ char **creds;
+ size_t i;
+ char *prompt;
+ char *reply;
+ size_t replylen;
+ int r;
+
+ // buf will be the libvirt URI. buf_len may be ignored.
+ printf ("Authentication required for libvirt conn '%s'\n",
+ buf);
+
+ // Ask libguestfs what credentials libvirt is demanding.
+ creds = guestfs_get_libvirt_requested_credentials (g);
+ if (creds == NULL)
+ exit (EXIT_FAILURE);
+
+ // Now ask the user for answers.
+ for (i = 0; creds[i] != NULL; ++i)
+ {
+ if (strcmp (creds[i], "authname") == 0 ||
+ strcmp (creds[i], "passphrase") == 0)
+ {
+ prompt =
+ guestfs_get_libvirt_requested_credential_prompt (g, i);
+ if (prompt && strcmp (prompt, "") != 0)
+ printf ("%s: ", prompt);
+ free (prompt);
+
+ // Some code here to ask for the credential.
+ // ...
+ // Put the reply in 'reply', length 'replylen' (bytes).
+
+ r = guestfs_set_libvirt_requested_credential (g, i,
+ reply, replylen);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+ }
+
+ free (creds[i]);
+ }
+
+ free (creds);
+ }
+
=head1 CANCELLING LONG TRANSFERS
Some operations can be cancelled by the caller while they are in
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
index 4f015e3..b7a75d3 100644
--- a/src/launch-libvirt.c
+++ b/src/launch-libvirt.c
@@ -157,8 +157,7 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri)
guestfs___print_timestamped_message (g, "connect to libvirt");
/* Connect to libvirt, get capabilities. */
- /* XXX Support libvirt authentication in the future. */
- conn = virConnectOpen (libvirt_uri);
+ conn = guestfs___open_libvirt_connection (g, libvirt_uri, 0);
if (!conn) {
libvirt_error (g, _("could not connect to libvirt (URI = %s)"),
libvirt_uri ? : "NULL");
diff --git a/src/libvirt-auth.c b/src/libvirt-auth.c
new file mode 100644
index 0000000..c3e3ddc
--- /dev/null
+++ b/src/libvirt-auth.c
@@ -0,0 +1,369 @@
+/* libguestfs
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_LIBVIRT
+#include <libvirt/libvirt.h>
+#include <libvirt/virterror.h>
+#endif
+
+#ifdef HAVE_LIBXML2
+#include <libxml/xpath.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#endif
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "guestfs-internal-actions.h"
+#include "guestfs_protocol.h"
+
+#if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2)
+
+static struct {
+ int credtype;
+ const char *credname;
+} libvirt_credential_types[NR_CREDENTIAL_TYPES] = {
+ { VIR_CRED_USERNAME, "username" },
+ { VIR_CRED_AUTHNAME, "authname" },
+ { VIR_CRED_LANGUAGE, "language" },
+ { VIR_CRED_CNONCE, "cnonce" },
+ { VIR_CRED_PASSPHRASE, "passphrase" },
+ { VIR_CRED_ECHOPROMPT, "echoprompt" },
+ { VIR_CRED_NOECHOPROMPT, "noechoprompt" },
+ { VIR_CRED_REALM, "realm" },
+ { VIR_CRED_EXTERNAL, "external" },
+};
+
+static int
+get_credtype_from_string (const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < NR_CREDENTIAL_TYPES; ++i)
+ if (STREQ (name, libvirt_credential_types[i].credname))
+ return libvirt_credential_types[i].credtype;
+
+ return -1;
+}
+
+static const char *
+get_string_of_credtype (int credtype)
+{
+ size_t i;
+
+ for (i = 0; i < NR_CREDENTIAL_TYPES; ++i)
+ if (credtype == libvirt_credential_types[i].credtype)
+ return libvirt_credential_types[i].credname;
+
+ return NULL;
+}
+
+/* Note to callers: Should it be possible to say that you don't
+ * support any libvirt credential types at all? Not clear if that's
+ * an error or not, so don't depend on the current behaviour.
+ */
+int
+guestfs__set_libvirt_supported_credentials (guestfs_h *g, char *const *creds)
+{
+ size_t i;
+ int credtype;
+
+ /* Try to make this call atomic so it either completely succeeds
+ * or if it fails it leaves the current state intact.
+ */
+ unsigned int ncredtypes = 0;
+ int credtypes[NR_CREDENTIAL_TYPES];
+
+ for (i = 0; creds[i] != NULL; ++i) {
+ credtype = get_credtype_from_string (creds[i]);
+ if (credtype == -1) {
+ error (g, _("unknown credential type '%s'"), creds[i]);
+ return -1;
+ }
+
+ if (ncredtypes >= NR_CREDENTIAL_TYPES) {
+ error (g, _("list of supported credentials is too long"));
+ return -1;
+ }
+
+ credtypes[ncredtypes++] = credtype;
+ }
+
+ g->nr_supported_credentials = ncredtypes;
+ memcpy (g->supported_credentials, credtypes, sizeof g->supported_credentials);
+
+ return 0;
+}
+
+/* This function is called back from libvirt. In turn it generates a
+ * libguestfs event to collect the desired credentials.
+ */
+static int
+libvirt_auth_callback (virConnectCredentialPtr cred,
+ unsigned int ncred,
+ void *gv)
+{
+ guestfs_h *g = gv;
+ size_t i;
+
+ if (cred == NULL || ncred == 0)
+ return 0;
+
+ /* libvirt probably does this already, but no harm in checking. */
+ for (i = 0; i < ncred; ++i)
+ cred[i].result = NULL;
+
+ g->requested_credentials = cred;
+ g->nr_requested_credentials = ncred;
+
+ guestfs___call_callbacks_message (g, GUESTFS_EVENT_LIBVIRT_AUTH,
+ g->saved_libvirt_uri,
+ strlen (g->saved_libvirt_uri));
+
+ /* libvirt documentation says: "Returns: 0 if all interactions were
+ * filled, or -1 upon error" However it also says "If an interaction
+ * cannot be filled, fill in NULL and 0". Does that mean it's OK
+ * (not an error) to leave a field NULL? libguestfs events cannot
+ * return errors, so that we would never have any other reason to
+ * return -1 here. XXX
+ */
+ for (i = 0; i < ncred; ++i)
+ if (cred[i].result == NULL)
+ goto error;
+ return 0;
+
+error:
+ for (i = 0; i < ncred; ++i) {
+ free (cred[i].result);
+ cred[i].result = NULL;
+ cred[i].resultlen = 0;
+ }
+ return -1;
+}
+
+static int
+exists_libvirt_auth_event (guestfs_h *g)
+{
+ size_t i;
+
+ for (i = 0; i < g->nr_events; ++i)
+ if ((g->events[i].event_bitmask & GUESTFS_EVENT_LIBVIRT_AUTH) != 0)
+ return 1;
+
+ return 0;
+}
+
+/* Open a libvirt connection (called from other parts of the library). */
+virConnectPtr
+guestfs___open_libvirt_connection (guestfs_h *g, const char *uri,
+ unsigned int flags)
+{
+ virConnectAuth authdata;
+ virConnectAuthPtr authdataptr;
+ virConnectPtr conn;
+
+ /* Did the caller register a GUESTFS_EVENT_LIBVIRT_AUTH event and
+ * call guestfs_set_libvirt_supported_credentials?
+ */
+ if (g->nr_supported_credentials > 0 && exists_libvirt_auth_event (g)) {
+ memset (&authdata, 0, sizeof authdata);
+ authdata.credtype = g->supported_credentials;
+ authdata.ncredtype = g->nr_supported_credentials;
+ authdata.cb = libvirt_auth_callback;
+ authdata.cbdata = g;
+ authdataptr = &authdata;
+ g->saved_libvirt_uri = uri;
+ }
+ else
+ authdataptr = virConnectAuthPtrDefault;
+
+ conn = virConnectOpenAuth (uri, authdataptr, flags);
+
+ /* Restore handle fields to "outside event handler" state. */
+ g->saved_libvirt_uri = NULL;
+ g->nr_requested_credentials = 0;
+ g->requested_credentials = NULL;
+
+ return conn;
+}
+
+/* The calls below are meant to be called recursively from
+ * the GUESTFS_EVENT_LIBVIRT_AUTH event.
+ */
+#define CHECK_IN_EVENT_HANDLER(r) \
+ if (g->nr_requested_credentials == 0) { \
+ error (g, _("%s should only be called from within the GUESTFS_EVENT_LIBVIRT_AUTH event handler"), \
+ __func__); \
+ return r; \
+ }
+
+char **
+guestfs__get_libvirt_requested_credentials (guestfs_h *g)
+{
+ char **ret;
+ size_t i;
+
+ CHECK_IN_EVENT_HANDLER (NULL);
+
+ /* Convert the requested_credentials types to a list of strings. */
+ ret = safe_malloc (g, sizeof (char *) * (g->nr_requested_credentials+1));
+ for (i = 0; i < g->nr_requested_credentials; ++i) {
+ ret[i] = safe_strdup (g,
+ get_string_of_credtype (g->requested_credentials[i].type));
+ }
+ ret[i] = NULL;
+
+ return ret; /* caller frees */
+}
+
+char *
+guestfs__get_libvirt_requested_credential_prompt (guestfs_h *g, int index)
+{
+ size_t i;
+
+ CHECK_IN_EVENT_HANDLER (NULL);
+
+ if (index >= 0 && (unsigned int) index < g->nr_requested_credentials)
+ i = (size_t) index;
+ else {
+ error (g, _("credential index out of range"));
+ return NULL;
+ }
+
+ if (g->requested_credentials[i].prompt)
+ return safe_strdup (g, g->requested_credentials[i].prompt);
+ else
+ return safe_strdup (g, "");
+}
+
+char *
+guestfs__get_libvirt_requested_credential_challenge (guestfs_h *g, int index)
+{
+ size_t i;
+
+ CHECK_IN_EVENT_HANDLER (NULL);
+
+ if (index >= 0 && (unsigned int) index < g->nr_requested_credentials)
+ i = (size_t) index;
+ else {
+ error (g, _("credential index out of range"));
+ return NULL;
+ }
+
+ if (g->requested_credentials[i].challenge)
+ return safe_strdup (g, g->requested_credentials[i].challenge);
+ else
+ return safe_strdup (g, "");
+}
+
+char *
+guestfs__get_libvirt_requested_credential_defresult (guestfs_h *g, int index)
+{
+ size_t i;
+
+ CHECK_IN_EVENT_HANDLER (NULL);
+
+ if (index >= 0 && (unsigned int) index < g->nr_requested_credentials)
+ i = (size_t) index;
+ else {
+ error (g, _("credential index out of range"));
+ return NULL;
+ }
+
+ if (g->requested_credentials[i].defresult)
+ return safe_strdup (g, g->requested_credentials[i].defresult);
+ else
+ return safe_strdup (g, "");
+}
+
+int
+guestfs__set_libvirt_requested_credential (guestfs_h *g, int index,
+ const char *cred, size_t cred_size)
+{
+ size_t i;
+
+ CHECK_IN_EVENT_HANDLER (-1);
+
+ if (index >= 0 && (unsigned int) index < g->nr_requested_credentials)
+ i = (size_t) index;
+ else {
+ error (g, _("credential index out of range"));
+ return -1;
+ }
+
+ /* All the evidence is that libvirt will free this. */
+ g->requested_credentials[i].result = safe_malloc (g, cred_size+1 /* sic */);
+ memcpy (g->requested_credentials[i].result, cred, cred_size);
+ /* Some libvirt drivers are buggy (eg. libssh2), and they expect
+ * that the cred field will be \0 terminated. To avoid surprises,
+ * add a \0 at the end.
+ */
+ g->requested_credentials[i].result[cred_size] = 0;
+ g->requested_credentials[i].resultlen = cred_size;
+ return 0;
+}
+
+#else /* no libvirt or libxml2 at compile time */
+
+#define NOT_IMPL(r) \
+ error (g, _("libvirt authentication APIs not available since this version of libguestfs was compiled without libvirt or libxml2")); \
+ return r
+
+int
+guestfs__set_libvirt_supported_credentials (guestfs_h *g, char *const *creds)
+{
+ NOT_IMPL(-1);
+}
+
+char **
+guestfs__get_libvirt_requested_credentials (guestfs_h *g)
+{
+ NOT_IMPL(NULL);
+}
+
+char *
+guestfs__get_libvirt_requested_credential_prompt (guestfs_h *g, int index)
+{
+ NOT_IMPL(NULL);
+}
+
+char *
+guestfs__get_libvirt_requested_credential_challenge (guestfs_h *g, int index)
+{
+ NOT_IMPL(NULL);
+}
+
+char *
+guestfs__get_libvirt_requested_credential_defresult (guestfs_h *g, int index)
+{
+ NOT_IMPL(NULL);
+}
+
+int
+guestfs__set_libvirt_requested_credential (guestfs_h *g, int index, const char *cred, size_t cred_size)
+{
+ NOT_IMPL(-1);
+}
+
+#endif /* no libvirt or libxml2 at compile time */
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c
index eecea26..f65686c 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -99,7 +99,7 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
}
/* Connect to libvirt, find the domain. */
- conn = virConnectOpenReadOnly (libvirturi);
+ conn = guestfs___open_libvirt_connection (g, libvirturi, VIR_CONNECT_RO);
if (!conn) {
err = virGetLastError ();
error (g, _("could not connect to libvirt (code %d, domain %d): %s"),
--
1.7.11.4
12 years, 1 month