Running qemu as a subprocess.
authorrjones <rjones>
Tue, 3 Mar 2009 15:35:50 +0000 (15:35 +0000)
committerrjones <rjones>
Tue, 3 Mar 2009 15:35:50 +0000 (15:35 +0000)
13 files changed:
.cvsignore
Makefile.am
autogen.sh
configure.ac
examples/.cvsignore [new file with mode: 0644]
examples/LICENSE [new file with mode: 0644]
examples/Makefile.am [new file with mode: 0644]
examples/df.c [new file with mode: 0644]
src/.cvsignore [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/guestfs.c [new file with mode: 0644]
src/guestfs.h [new file with mode: 0644]
src/guestfs_protocol.x [new file with mode: 0644]

index 286d115..9707adc 100644 (file)
@@ -1,3 +1,4 @@
+.deps
 Makefile.in
 Makefile
 aclocal.m4
@@ -7,4 +8,5 @@ config.h.in
 config.log
 config.status
 configure
+libtool
 stamp-h1
\ No newline at end of file
index ae5ba41..cb1fdd2 100644 (file)
@@ -15,3 +15,4 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+SUBDIRS = src examples
index 959fdce..8d86dbe 100755 (executable)
@@ -21,6 +21,8 @@
 set -e
 set -v
 export AUTOMAKE='automake --foreign'
+aclocal
+libtoolize
 autoreconf
 cd daemon
 autoreconf
\ No newline at end of file
index 45f1e04..7b9db49 100644 (file)
@@ -17,6 +17,7 @@
 
 AC_INIT([libguestfs],[0.1])
 AM_INIT_AUTOMAKE
+AC_PROG_LIBTOOL
 
 dnl Make sure the user has created the link to nfs-utils source.
 if ! test -e $srcdir/daemon/nfs-utils \
@@ -35,6 +36,9 @@ test "x$U" != "x" && AC_MSG_ERROR([Compiler not ANSI compliant])
 
 AC_PROG_CC_C_O
 
+dnl Headers.
+AC_CHECK_HEADERS([errno.h sys/types.h sys/un.h sys/wait.h sys/socket.h])
+
 dnl Check for rpcgen and XDR library.  rpcgen is optional.
 AC_CHECK_PROG([RPCGEN],[rpcgen],[rpcgen],[no])
 AM_CONDITIONAL([RPCGEN],[test "x$RPCGEN" != "xno"])
@@ -49,6 +53,7 @@ dnl on several factors explained in the README.
 AC_PATH_PROG([QEMU],[qemu],[no],
        [$PATH$PATH_SEPARATOR/usr/sbin$PATH_SEPARATOR/sbin])
 test "x$QEMU" = "xno" && AC_MSG_ERROR([No 'qemu' program found])
+AC_DEFINE_UNQUOTED([QEMU],["$QEMU"],[Location of qemu binary.])
 
 dnl Check for mkinitrd, cpio.
 AC_PATH_PROG([MKINITRD],[mkinitrd],[no],
@@ -59,5 +64,5 @@ test "x$CPIO" = "xno" && AC_MSG_ERROR([No 'cpio' program found])
 
 dnl Produce output files.
 AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile])
 AC_OUTPUT
diff --git a/examples/.cvsignore b/examples/.cvsignore
new file mode 100644 (file)
index 0000000..d8d6624
--- /dev/null
@@ -0,0 +1,5 @@
+.deps
+.libs
+Makefile
+Makefile.in
+df
\ No newline at end of file
diff --git a/examples/LICENSE b/examples/LICENSE
new file mode 100644 (file)
index 0000000..5ba695a
--- /dev/null
@@ -0,0 +1,2 @@
+All the examples in the examples/ subdirectory may be freely copied
+without any restrictions.
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644 (file)
index 0000000..66a32d7
--- /dev/null
@@ -0,0 +1,7 @@
+# libguestfs examples
+
+noinst_PROGRAMS = df
+
+df_SOURCES = df.c
+df_CFLAGS = -I$(top_builddir)/src
+df_LDADD = $(top_builddir)/src/libguestfs.la
diff --git a/examples/df.c b/examples/df.c
new file mode 100644 (file)
index 0000000..818de6e
--- /dev/null
@@ -0,0 +1,36 @@
+/* A simple "df" command for guests. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <guestfs.h>
+
+int
+main (int argc, char *argv[])
+{
+  guestfs_h *g;
+
+  if (argc != 2 || access (argv[1], F_OK) != 0) {
+    fprintf (stderr, "Usage: df disk-image\n");
+    exit (1);
+  }
+
+  g = guestfs_create ();
+  if (!g) {
+    perror ("guestfs_create");
+    exit (1);
+  }
+
+  guestfs_set_exit_on_error (g, 1);
+  guestfs_set_verbose (g, 1);
+
+  guestfs_add_drive (g, argv[1]);
+
+  guestfs_wait_ready (g);
+
+
+
+
+  guestfs_free (g);
+  return 0;
+}
diff --git a/src/.cvsignore b/src/.cvsignore
new file mode 100644 (file)
index 0000000..c45d1c0
--- /dev/null
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+libguestfs.la
+*.lo
\ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..d900e3a
--- /dev/null
@@ -0,0 +1,20 @@
+# libguestfs
+# 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.
+
+lib_LTLIBRARIES = libguestfs.la
+
+libguestfs_la_SOURCES = guestfs.c guestfs.h
diff --git a/src/guestfs.c b/src/guestfs.c
new file mode 100644 (file)
index 0000000..e18021f
--- /dev/null
@@ -0,0 +1,508 @@
+/* libguestfs
+ * Copyright (C) 2009 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>
+
+#define _BSD_SOURCE /* for mkdtemp, usleep */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include "guestfs.h"
+
+static int error (guestfs_h *g, const char *fs, ...);
+static int perrorf (guestfs_h *g, const char *fs, ...);
+static void *safe_malloc (guestfs_h *g, int nbytes);
+static void *safe_realloc (guestfs_h *g, void *ptr, int nbytes);
+static char *safe_strdup (guestfs_h *g, const char *str);
+
+/* GuestFS handle and connection. */
+struct guestfs_h
+{
+  /* All these socks/pids are -1 if not connected. */
+  int sock;                    /* Daemon communications socket. */
+  int pid;                     /* Qemu PID. */
+
+  char *tmpdir;                        /* Temporary directory containing logfile
+                                * and socket.  Cleaned up unless there is
+                                * an error.
+                                */
+
+  char **cmdline;              /* Qemu command line. */
+  int cmdline_size;
+
+  guestfs_abort_fn abort_fn;
+  int exit_on_error;
+  int verbose;
+};
+
+guestfs_h *
+guestfs_create (void)
+{
+  guestfs_h *g;
+
+  g = malloc (sizeof (*g));
+  if (!g) return NULL;
+
+  g->sock = -1;
+  g->pid = -1;
+
+  g->tmpdir = NULL;
+
+  g->abort_fn = abort;         /* Have to set these before safe_malloc. */
+  g->exit_on_error = 0;
+  g->verbose = getenv ("LIBGUESTFS_VERBOSE") != NULL;
+
+  g->cmdline = safe_malloc (g, sizeof (char *) * 1);
+  g->cmdline_size = 1;
+  g->cmdline[0] = NULL;                /* This is chosen by guestfs_launch. */
+
+  return g;
+}
+
+void
+guestfs_free (guestfs_h *g)
+{
+  int i;
+  char filename[256];
+
+  if (g->pid) guestfs_kill_subprocess (g);
+
+  /* The assumption is that programs calling this have successfully
+   * used qemu, so delete the logfile and socket directory.
+   */
+  if (g->tmpdir) {
+    snprintf (filename, sizeof filename, "%s/sock", g->tmpdir);
+    unlink (filename);
+
+    snprintf (filename, sizeof filename, "%s/qemu.log", g->tmpdir);
+    unlink (filename);
+
+    rmdir (g->tmpdir);
+
+    free (g->tmpdir);
+  }
+
+  for (i = 0; i < g->cmdline_size; ++i)
+    free (g->cmdline[i]);
+  free (g->cmdline);
+
+  free (g);
+}
+
+/* Cleanup fds and sockets, assuming the subprocess is dead already. */
+static void
+cleanup_fds (guestfs_h *g)
+{
+  if (g->sock >= 0) close (g->sock);
+  g->sock = -1;
+}
+
+/* Wait for subprocess to exit. */
+static void
+wait_subprocess (guestfs_h *g)
+{
+  if (g->pid >= 0) waitpid (g->pid, NULL, 0);
+  g->pid = -1;
+}
+
+static int
+error (guestfs_h *g, const char *fs, ...)
+{
+  va_list args;
+
+  fprintf (stderr, "libguestfs: ");
+  va_start (args, fs);
+  vfprintf (stderr, fs, args);
+  va_end (args);
+  fputc ('\n', stderr);
+
+  if (g->exit_on_error) exit (1);
+  return -1;
+}
+
+static int
+perrorf (guestfs_h *g, const char *fs, ...)
+{
+  va_list args;
+  char buf[256];
+  int err = errno;
+
+  fprintf (stderr, "libguestfs: ");
+  va_start (args, fs);
+  vfprintf (stderr, fs, args);
+  va_end (args);
+  strerror_r (err, buf, sizeof buf);
+  fprintf (stderr, ": %s\n", buf);
+
+  if (g->exit_on_error) exit (1);
+  return -1;
+}
+
+static void *
+safe_malloc (guestfs_h *g, int nbytes)
+{
+  void *ptr = malloc (nbytes);
+  if (!ptr) g->abort_fn ();
+  return ptr;
+}
+
+static void *
+safe_realloc (guestfs_h *g, void *ptr, int nbytes)
+{
+  void *p = realloc (ptr, nbytes);
+  if (!p) g->abort_fn ();
+  return p;
+}
+
+static char *
+safe_strdup (guestfs_h *g, const char *str)
+{
+  char *s = strdup (str);
+  if (!s) g->abort_fn ();
+  return s;
+}
+
+void
+guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_fn a)
+{
+  g->abort_fn = a;
+}
+
+guestfs_abort_fn
+guestfs_get_out_of_memory_handler (guestfs_h *g)
+{
+  return g->abort_fn;
+}
+
+void
+guestfs_set_exit_on_error (guestfs_h *g, int e)
+{
+  g->exit_on_error = e;
+}
+
+int
+guestfs_get_exit_on_error (guestfs_h *g)
+{
+  return g->exit_on_error;
+}
+
+void
+guestfs_set_verbose (guestfs_h *g, int v)
+{
+  g->verbose = v;
+}
+
+int
+guestfs_get_verbose (guestfs_h *g)
+{
+  return g->verbose;
+}
+
+/* Add an escaped string to the current command line. */
+static int
+add_cmdline (guestfs_h *g, const char *str)
+{
+  if (g->pid >= 0)
+    return error (g, "command line cannot be altered after qemu subprocess launched");
+
+  g->cmdline_size++;
+  g->cmdline = safe_realloc (g, g->cmdline, sizeof (char *) * g->cmdline_size);
+  g->cmdline[g->cmdline_size-1] = safe_strdup (g, str);
+
+  return 0;
+}
+
+int
+guestfs_config (guestfs_h *g,
+               const char *qemu_param, const char *qemu_value)
+{
+  if (qemu_param[0] != '-')
+    return error (g, "guestfs_config: parameter must begin with '-' character");
+
+  /* A bit fascist, but the user will probably break the extra
+   * parameters that we add if they try to set any of these.
+   */
+  if (strcmp (qemu_param, "-kernel") == 0 ||
+      strcmp (qemu_param, "-initrd") == 0 ||
+      strcmp (qemu_param, "-nographic") == 0 ||
+      strcmp (qemu_param, "-serial") == 0 ||
+      strcmp (qemu_param, "-vnc") == 0 ||
+      strcmp (qemu_param, "-full-screen") == 0 ||
+      strcmp (qemu_param, "-std-vga") == 0 ||
+      strcmp (qemu_param, "-vnc") == 0)
+    return error (g, "guestfs_config: parameter '%s' isn't allowed");
+
+  if (add_cmdline (g, qemu_param) != 0) return -1;
+
+  if (qemu_value != NULL) {
+    if (add_cmdline (g, qemu_value) != 0) return -1;
+  }
+
+  return 0;
+}
+
+int
+guestfs_add_drive (guestfs_h *g, const char *filename)
+{
+  int len = strlen (filename) + 64;
+  char buf[len];
+
+  if (strchr (filename, ',') != NULL)
+    return error (g, "filename cannot contain ',' (comma) character");
+
+  snprintf (buf, len, "file=%s,media=disk", filename);
+
+  return guestfs_config (g, "-drive", buf);
+}
+
+int
+guestfs_add_cdrom (guestfs_h *g, const char *filename)
+{
+  int len = strlen (filename) + 64;
+  char buf[len];
+
+  if (strchr (filename, ',') != NULL)
+    return error (g, "filename cannot contain ',' (comma) character");
+
+  snprintf (buf, len, "file=%s,if=ide,index=1,media=cdrom", filename);
+
+  return guestfs_config (g, "-drive", buf);
+}
+
+int
+guestfs_launch (guestfs_h *g)
+{
+  static const char *dir_template = "/tmp/libguestfsXXXXXX";
+  int r, i;
+  const char *qemu = QEMU;     /* XXX */
+  const char *kernel = "/boot/vmlinuz-2.6.27.15-170.2.24.fc10.x86_64";
+  const char *initrd = "/boot/initrd-2.6.27.15-170.2.24.fc10.x86_64.img";
+  char unixsock[256];
+  char vmchannel[256];
+  char tmpfile[256];
+
+  /* XXX Choose which qemu to run. */
+  /* XXX Choose initrd, etc. */
+
+  /* Make the temporary directory containing the logfile and socket. */
+  if (!g->tmpdir) {
+    g->tmpdir = safe_strdup (g, dir_template);
+    if (mkdtemp (g->tmpdir) == NULL)
+      return perrorf (g, "%s: cannot create temporary directory", dir_template);
+
+    snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir);
+  }
+
+  r = fork ();
+  if (r == -1)
+    return perrorf (g, "fork");
+
+  if (r > 0) {                 /* Parent (library). */
+    g->pid = r;
+
+    /* If qemu is going to die during startup, give it a tiny amount of
+     * time to print the error message.
+     */
+    usleep (10000);
+  } else {                     /* Child (qemu). */
+    /* Set up the full command line.  Do this in the subprocess so we
+     * don't need to worry about cleaning up.
+     */
+    g->cmdline[0] = (char *) qemu;
+
+    g->cmdline = realloc (g->cmdline, sizeof (char *) * (g->cmdline_size + 14));
+    if (g->cmdline == NULL) {
+      perror ("realloc");
+      _exit (1);
+    }
+
+    snprintf (vmchannel, sizeof vmchannel,
+             "channel,%d:unix:%s,server,nowait", 666, unixsock);
+
+    g->cmdline[g->cmdline_size   ] = "-kernel";
+    g->cmdline[g->cmdline_size+ 1] = (char *) kernel;
+    g->cmdline[g->cmdline_size+ 2] = "-initrd";
+    g->cmdline[g->cmdline_size+ 3] = (char *) initrd;
+    g->cmdline[g->cmdline_size+ 4] = "-append";
+    g->cmdline[g->cmdline_size+ 5] = "console=ttyS0";
+    g->cmdline[g->cmdline_size+ 6] = "-nographic";
+    g->cmdline[g->cmdline_size+ 7] = "-serial";
+    g->cmdline[g->cmdline_size+ 8] = "stdio";
+    g->cmdline[g->cmdline_size+ 9] = "-net";
+    g->cmdline[g->cmdline_size+10] = vmchannel;
+    g->cmdline[g->cmdline_size+11] = "-net";
+    g->cmdline[g->cmdline_size+12] = "user,vlan0";
+    g->cmdline[g->cmdline_size+13] = NULL;
+
+    if (g->verbose) {
+      fprintf (stderr, "Running %s", qemu);
+      for (i = 0; g->cmdline[i]; ++i)
+       fprintf (stderr, " %s", g->cmdline[i]);
+      fprintf (stderr, "\n");
+    }
+
+    /* Set up stdin, stdout.  Messages should go to the logfile. */
+    close (0);
+    close (1);
+    open ("/dev/null", O_RDONLY);
+    snprintf (tmpfile, sizeof tmpfile, "%s/qemu.log", g->tmpdir);
+    open (tmpfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
+    /*dup2 (1, 2);*/
+
+    execv (qemu, g->cmdline);  /* Run qemu. */
+    perror (qemu);
+    _exit (1);
+  }
+
+  return 0;
+}
+
+#define UNIX_PATH_MAX 108
+
+int
+guestfs_wait_ready (guestfs_h *g)
+{
+  int r, i, lsock;
+  struct sockaddr_un addr;
+
+  if (guestfs_ready (g)) return 0;
+
+  /* Launch the subprocess, if there isn't one already. */
+  if (g->pid == -1) {
+    if (guestfs_launch (g) != 0)
+      return -1;
+  }
+
+  if (g->sock >= 0) {
+    close (g->sock);
+    g->sock = -1;
+  }
+
+  lsock = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (lsock == -1)
+    return perrorf (g, "socket");
+
+  addr.sun_family = AF_UNIX;
+  snprintf (addr.sun_path, UNIX_PATH_MAX, "%s/sock", g->tmpdir);
+
+  if (bind (lsock, (struct sockaddr *) &addr, sizeof addr) == -1) {
+    perrorf (g, "bind");
+    close (lsock);
+    return -1;
+  }
+
+  if (listen (lsock, 1) == -1) {
+    perrorf (g, "listen");
+    close (lsock);
+    return -1;
+  }
+
+  if (fcntl (lsock, F_SETFL, O_NONBLOCK) == -1) {
+    perrorf (g, "set socket non-blocking");
+    close (lsock);
+    return -1;
+  }
+
+  /* Wait until the daemon running inside the guest connects to the
+   * Unix socket, which indicates it's alive.  Qemu might exit in the
+   * meantime if there is a problem.  More problematically qemu might
+   * hang, which we can only detect by timeout.
+   */
+  for (i = 0; i < 30; ++i) {
+    r = waitpid (g->pid, NULL, WNOHANG);
+
+    if (r > 0 || (r == -1 && errno == ECHILD)) {
+      error (g, "qemu subprocess exited unexpectedly during initialization");
+      g->pid = -1;
+      cleanup_fds (g);
+      close (lsock);
+      return -1;
+    }
+
+    r = accept (lsock, NULL, 0);
+    if (r >= 0) {
+      g->sock = r;
+      fcntl (g->sock, F_SETFL, O_NONBLOCK);
+      close (lsock);
+      return 0;
+    }
+    if (errno == EAGAIN) {
+      sleep (1);
+      continue;
+    }
+    perrorf (g, "accept");
+    close (lsock);
+    guestfs_kill_subprocess (g);
+    return -1;
+  }
+
+  close (lsock);
+  return error (g, "timeout waiting for guest to become ready");
+}
+
+int
+guestfs_ready (guestfs_h *g)
+{
+  return
+    g->pid >= 0 &&
+    kill (g->pid, 0) == 0 &&
+    g->sock >= 0 /* &&
+    guestfs_ping_daemon (g) >= 0 */;
+}
+
+int
+guestfs_kill_subprocess (guestfs_h *g)
+{
+  if (g->pid >= 0) {
+    if (g->verbose)
+      fprintf (stderr, "sending SIGINT to pid %d\n", g->pid);
+
+    kill (g->pid, SIGINT);
+    wait_subprocess (g);
+  }
+
+  cleanup_fds (g);
+
+  return 0;
+}
diff --git a/src/guestfs.h b/src/guestfs.h
new file mode 100644 (file)
index 0000000..9b84e31
--- /dev/null
@@ -0,0 +1,55 @@
+/* libguestfs
+ * Copyright (C) 2009 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
+ */
+
+#ifndef GUESTFS_H_
+#define GUESTFS_H_
+
+/* For API documentation, please read the manual page guestfs(3). */
+
+typedef struct guestfs_h guestfs_h;
+
+/* Create and destroy the guest handle. */
+extern guestfs_h *guestfs_create (void);
+extern void guestfs_free (guestfs_h *g);
+
+/* Guest configuration. */
+extern int guestfs_config (guestfs_h *g,
+                          const char *qemu_param, const char *qemu_value);
+extern int guestfs_add_drive (guestfs_h *g, const char *filename);
+extern int guestfs_add_cdrom (guestfs_h *g, const char *filename);
+
+/* Steps to start up the guest. */
+extern int guestfs_launch (guestfs_h *g);
+extern int guestfs_wait_ready (guestfs_h *g);
+extern int guestfs_ready (guestfs_h *g);
+
+/* Kill the guest subprocess. */
+extern int guestfs_kill_subprocess (guestfs_h *g);
+
+/* Error handling. */
+typedef void (*guestfs_abort_fn) (void);
+extern void guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_fn);
+extern guestfs_abort_fn guestfs_get_out_of_memory_handler (guestfs_h *g);
+
+extern void guestfs_set_exit_on_error (guestfs_h *g, int exit_on_error);
+extern int guestfs_get_exit_on_error (guestfs_h *g);
+
+extern void guestfs_set_verbose (guestfs_h *g, int verbose);
+extern int guestfs_get_verbose (guestfs_h *g);
+
+#endif /* GUESTFS_H_ */
diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x
new file mode 100644 (file)
index 0000000..59bad58
--- /dev/null
@@ -0,0 +1,22 @@
+/* libguestfs
+ * Copyright (C) 2009 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
+ *
+ * This file describes the client [library] to server [daemon in
+ * guest] protocol.  As far as possible, all code in the library is
+ * automatically generated from this protocol description using
+ * rpcgen and the perl script 'generator.pl'.
+ */