summaryrefslogtreecommitdiffstats
path: root/src/handle.c
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2012-11-12 12:56:39 +0000
committerRichard W.M. Jones <rjones@redhat.com>2012-11-12 13:40:25 +0000
commit02ecd048d3caf7804361bb0f5dca071f97aefaa1 (patch)
tree2b17e6b2037297bc1bc93b4371be15a3b9d3aef6 /src/handle.c
parentf9ab256f0e4c1197b505b0249e66e7614644bd0b (diff)
downloadlibguestfs-02ecd048d3caf7804361bb0f5dca071f97aefaa1.tar.gz
libguestfs-02ecd048d3caf7804361bb0f5dca071f97aefaa1.tar.xz
libguestfs-02ecd048d3caf7804361bb0f5dca071f97aefaa1.zip
lib: Split up huge src/guestfs.c into logical compilation units.
This file had grown by accretion to include: - code related to handles (now in src/handle.c) - safe allocation (src/alloc.c) - debug, errors, warnings (src/errors.c) - private data (src/private-data.c) - miscellaneous functions (src/canonical-name.c, src/utils.c) This commit also removes about a dozen #include files which were probably not really used. This is just code motion.
Diffstat (limited to 'src/handle.c')
-rw-r--r--src/handle.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/src/handle.c b/src/handle.c
new file mode 100644
index 00000000..fd7db698
--- /dev/null
+++ b/src/handle.c
@@ -0,0 +1,669 @@
+/* libguestfs
+ * Copyright (C) 2009-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 <unistd.h>
+#include <string.h>
+
+#ifdef HAVE_LIBVIRT
+#include <libvirt/libvirt.h>
+#endif
+
+#ifdef HAVE_LIBXML2
+#include <libxml/parser.h>
+#include <libxml/xmlversion.h>
+#endif
+
+#include "glthread/lock.h"
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "guestfs-internal-actions.h"
+#include "guestfs_protocol.h"
+
+static int parse_attach_method (guestfs_h *g, const char *method);
+static int shutdown_backend (guestfs_h *g, int check_for_errors);
+static void close_handles (void);
+
+gl_lock_define_initialized (static, handles_lock);
+static guestfs_h *handles = NULL;
+static int atexit_handler_set = 0;
+
+gl_lock_define_initialized (static, init_lock);
+
+/* No initialization is required by libguestfs, but libvirt and
+ * libxml2 require initialization if they might be called from
+ * multiple threads. Hence this constructor function which is called
+ * when libguestfs is first loaded.
+ */
+static void init_libguestfs (void) __attribute__((constructor));
+
+static void
+init_libguestfs (void)
+{
+#if defined(HAVE_LIBVIRT) || defined(HAVE_LIBXML2)
+ gl_lock_lock (init_lock);
+#endif
+#ifdef HAVE_LIBVIRT
+ virInitialize ();
+#endif
+#ifdef HAVE_LIBXML2
+ xmlInitParser ();
+ LIBXML_TEST_VERSION;
+#endif
+#if defined(HAVE_LIBVIRT) || defined(HAVE_LIBXML2)
+ gl_lock_unlock (init_lock);
+#endif
+}
+
+guestfs_h *
+guestfs_create (void)
+{
+ return guestfs_create_flags (0);
+}
+
+guestfs_h *
+guestfs_create_flags (unsigned flags, ...)
+{
+ guestfs_h *g;
+
+ g = calloc (1, sizeof (*g));
+ if (!g) return NULL;
+
+ g->state = CONFIG;
+
+ g->fd[0] = -1;
+ g->fd[1] = -1;
+ g->sock = -1;
+
+ guestfs___init_error_handler (g);
+ g->abort_cb = abort;
+
+ g->recovery_proc = 1;
+ g->autosync = 1;
+
+ g->memsize = 500;
+
+ /* Start with large serial numbers so they are easy to spot
+ * inside the protocol.
+ */
+ g->msg_next_serial = 0x00123400;
+
+ /* Default is uniprocessor appliance. */
+ g->smp = 1;
+
+ g->path = strdup (GUESTFS_DEFAULT_PATH);
+ if (!g->path) goto error;
+
+ g->qemu = strdup (QEMU);
+ if (!g->qemu) goto error;
+
+ if (parse_attach_method (g, DEFAULT_ATTACH_METHOD) == -1) {
+ warning (g, _("libguestfs was built with an invalid default attach-method, using 'appliance' instead"));
+ g->attach_method = ATTACH_METHOD_APPLIANCE;
+ }
+
+ if (!(flags & GUESTFS_CREATE_NO_ENVIRONMENT))
+ guestfs_parse_environment (g);
+
+ if (!(flags & GUESTFS_CREATE_NO_CLOSE_ON_EXIT)) {
+ g->close_on_exit = true;
+
+ /* Link the handles onto a global list. */
+ gl_lock_lock (handles_lock);
+ g->next = handles;
+ handles = g;
+ if (!atexit_handler_set) {
+ atexit (close_handles);
+ atexit_handler_set = 1;
+ }
+ gl_lock_unlock (handles_lock);
+ }
+
+ debug (g, "create: flags = %u, handle = %p", flags, g);
+
+ return g;
+
+ error:
+ free (g->attach_method_arg);
+ free (g->path);
+ free (g->qemu);
+ free (g->append);
+ free (g);
+ return NULL;
+}
+
+static int
+parse_environment (guestfs_h *g,
+ char *(*do_getenv) (const void *data, const char *),
+ const void *data)
+{
+ int memsize;
+ char *str;
+
+ /* Don't bother checking the return values of functions
+ * that cannot return errors.
+ */
+
+ str = do_getenv (data, "LIBGUESTFS_TRACE");
+ if (str != NULL && STREQ (str, "1"))
+ guestfs_set_trace (g, 1);
+
+ str = do_getenv (data, "LIBGUESTFS_DEBUG");
+ if (str != NULL && STREQ (str, "1"))
+ guestfs_set_verbose (g, 1);
+
+ str = do_getenv (data, "LIBGUESTFS_TMPDIR");
+ if (str)
+ guestfs_set_tmpdir (g, str);
+
+ str = do_getenv (data, "LIBGUESTFS_CACHEDIR");
+ if (str)
+ guestfs_set_cachedir (g, str);
+
+ free (g->env_tmpdir);
+ str = do_getenv (data, "TMPDIR");
+ g->env_tmpdir = str ? safe_strdup (g, str) : NULL;
+
+ str = do_getenv (data, "LIBGUESTFS_PATH");
+ if (str)
+ guestfs_set_path (g, str);
+
+ str = do_getenv (data, "LIBGUESTFS_QEMU");
+ if (str)
+ guestfs_set_qemu (g, str);
+
+ str = do_getenv (data, "LIBGUESTFS_APPEND");
+ if (str)
+ guestfs_set_append (g, str);
+
+ str = do_getenv (data, "LIBGUESTFS_MEMSIZE");
+ if (str) {
+ if (sscanf (str, "%d", &memsize) != 1 || memsize < 128) {
+ error (g, "non-numeric or too small value for LIBGUESTFS_MEMSIZE");
+ return -1;
+ }
+ guestfs_set_memsize (g, memsize);
+ }
+
+ str = do_getenv (data, "LIBGUESTFS_ATTACH_METHOD");
+ if (str) {
+ if (guestfs_set_attach_method (g, str) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *
+call_getenv (const void *data, const char *name)
+{
+ return getenv (name);
+}
+
+int
+guestfs__parse_environment (guestfs_h *g)
+{
+ return parse_environment (g, call_getenv, NULL);
+}
+
+static char *
+getenv_from_strings (const void *stringsv, const char *name)
+{
+ char **strings = (char **) stringsv;
+ size_t len = strlen (name);
+ size_t i;
+
+ for (i = 0; strings[i] != NULL; ++i)
+ if (STRPREFIX (strings[i], name) && strings[i][len] == '=')
+ return (char *) &strings[i][len+1];
+ return NULL;
+}
+
+int
+guestfs__parse_environment_list (guestfs_h *g, char * const *strings)
+{
+ return parse_environment (g, getenv_from_strings, strings);
+}
+
+void
+guestfs_close (guestfs_h *g)
+{
+ struct qemu_param *qp, *qp_next;
+ guestfs_h **gg;
+
+ if (g->state == NO_HANDLE) {
+ /* Not safe to call ANY callbacks here, so ... */
+ fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
+ return;
+ }
+
+ /* Remove the handle from the handles list. */
+ if (g->close_on_exit) {
+ gl_lock_lock (handles_lock);
+ for (gg = &handles; *gg != g; gg = &(*gg)->next)
+ ;
+ *gg = g->next;
+ gl_lock_unlock (handles_lock);
+ }
+
+ if (g->trace) {
+ const char trace_msg[] = "close";
+
+ guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE,
+ trace_msg, strlen (trace_msg));
+ }
+
+ debug (g, "closing guestfs handle %p (state %d)", g, g->state);
+
+ /* If we are valgrinding the daemon, then we *don't* want to kill
+ * the subprocess because we want the final valgrind messages sent
+ * when we close sockets below. However for normal production use,
+ * killing the subprocess is the right thing to do (in case the
+ * daemon or qemu is not responding).
+ */
+#ifndef VALGRIND_DAEMON
+ if (g->state != CONFIG)
+ shutdown_backend (g, 0);
+#endif
+
+ /* Run user close callbacks. */
+ guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE);
+
+ /* Test output file used by bindtests. */
+ if (g->test_fp != NULL)
+ fclose (g->test_fp);
+
+ /* Remove temporary directory. */
+ guestfs___remove_tmpdir (g);
+
+ /* Mark the handle as dead and then free up all memory. */
+ g->state = NO_HANDLE;
+
+ free (g->events);
+ g->nr_events = 0;
+ g->events = NULL;
+
+#if HAVE_FUSE
+ guestfs___free_fuse (g);
+#endif
+
+ guestfs___free_inspect_info (g);
+ guestfs___free_drives (g);
+
+ for (qp = g->qemu_params; qp; qp = qp_next) {
+ free (qp->qemu_param);
+ free (qp->qemu_value);
+ qp_next = qp->next;
+ free (qp);
+ }
+
+ while (g->error_cb_stack)
+ guestfs_pop_error_handler (g);
+
+ if (g->pda)
+ hash_free (g->pda);
+ free (g->tmpdir);
+ free (g->env_tmpdir);
+ free (g->int_tmpdir);
+ free (g->int_cachedir);
+ free (g->last_error);
+ free (g->path);
+ free (g->qemu);
+ free (g->append);
+ free (g);
+}
+
+int
+guestfs__shutdown (guestfs_h *g)
+{
+ return shutdown_backend (g, 1);
+}
+
+/* guestfs_shutdown calls shutdown_backend with check_for_errors = 1.
+ * guestfs_close calls shutdown_backend with check_for_errors = 0.
+ *
+ * 'check_for_errors' is a hint to the backend about whether we care
+ * about errors or not. In the libvirt case it can be used to
+ * optimize the shutdown for speed when we don't care.
+ */
+static int
+shutdown_backend (guestfs_h *g, int check_for_errors)
+{
+ int ret = 0;
+
+ if (g->state == CONFIG)
+ return 0;
+
+ /* Try to sync if autosync flag is set. */
+ if (g->autosync && g->state == READY) {
+ if (guestfs_internal_autosync (g) == -1)
+ ret = -1;
+ }
+
+ /* Close sockets. */
+ if (g->fd[0] >= 0)
+ close (g->fd[0]);
+ if (g->fd[1] >= 0)
+ close (g->fd[1]);
+ if (g->sock >= 0)
+ close (g->sock);
+ g->fd[0] = -1;
+ g->fd[1] = -1;
+ g->sock = -1;
+
+ if (g->attach_ops->shutdown (g, check_for_errors) == -1)
+ ret = -1;
+
+ guestfs___free_drives (g);
+
+ g->state = CONFIG;
+
+ return ret;
+}
+
+/* Close all open handles (called from atexit(3)). */
+static void
+close_handles (void)
+{
+ while (handles) guestfs_close (handles);
+}
+
+void
+guestfs_user_cancel (guestfs_h *g)
+{
+ g->user_cancel = 1;
+}
+
+int
+guestfs__set_verbose (guestfs_h *g, int v)
+{
+ g->verbose = !!v;
+ return 0;
+}
+
+int
+guestfs__get_verbose (guestfs_h *g)
+{
+ return g->verbose;
+}
+
+int
+guestfs__set_autosync (guestfs_h *g, int a)
+{
+ g->autosync = !!a;
+ return 0;
+}
+
+int
+guestfs__get_autosync (guestfs_h *g)
+{
+ return g->autosync;
+}
+
+int
+guestfs__set_path (guestfs_h *g, const char *path)
+{
+ free (g->path);
+ g->path = NULL;
+
+ g->path =
+ path == NULL ?
+ safe_strdup (g, GUESTFS_DEFAULT_PATH) : safe_strdup (g, path);
+ return 0;
+}
+
+const char *
+guestfs__get_path (guestfs_h *g)
+{
+ return g->path;
+}
+
+int
+guestfs__set_qemu (guestfs_h *g, const char *qemu)
+{
+ free (g->qemu);
+ g->qemu = NULL;
+
+ g->qemu = qemu == NULL ? safe_strdup (g, QEMU) : safe_strdup (g, qemu);
+ return 0;
+}
+
+const char *
+guestfs__get_qemu (guestfs_h *g)
+{
+ return g->qemu;
+}
+
+int
+guestfs__set_append (guestfs_h *g, const char *append)
+{
+ free (g->append);
+ g->append = NULL;
+
+ g->append = append ? safe_strdup (g, append) : NULL;
+ return 0;
+}
+
+const char *
+guestfs__get_append (guestfs_h *g)
+{
+ return g->append;
+}
+
+int
+guestfs__set_memsize (guestfs_h *g, int memsize)
+{
+ g->memsize = memsize;
+ return 0;
+}
+
+int
+guestfs__get_memsize (guestfs_h *g)
+{
+ return g->memsize;
+}
+
+int
+guestfs__set_selinux (guestfs_h *g, int selinux)
+{
+ g->selinux = selinux;
+ return 0;
+}
+
+int
+guestfs__get_selinux (guestfs_h *g)
+{
+ return g->selinux;
+}
+
+struct guestfs_version *
+guestfs__version (guestfs_h *g)
+{
+ struct guestfs_version *r;
+
+ r = safe_malloc (g, sizeof *r);
+ r->major = PACKAGE_VERSION_MAJOR;
+ r->minor = PACKAGE_VERSION_MINOR;
+ r->release = PACKAGE_VERSION_RELEASE;
+ r->extra = safe_strdup (g, PACKAGE_VERSION_EXTRA);
+ return r;
+}
+
+int
+guestfs__set_trace (guestfs_h *g, int t)
+{
+ g->trace = !!t;
+ return 0;
+}
+
+int
+guestfs__get_trace (guestfs_h *g)
+{
+ return g->trace;
+}
+
+int
+guestfs__set_direct (guestfs_h *g, int d)
+{
+ g->direct = !!d;
+ return 0;
+}
+
+int
+guestfs__get_direct (guestfs_h *g)
+{
+ return g->direct;
+}
+
+int
+guestfs__set_recovery_proc (guestfs_h *g, int f)
+{
+ g->recovery_proc = !!f;
+ return 0;
+}
+
+int
+guestfs__get_recovery_proc (guestfs_h *g)
+{
+ return g->recovery_proc;
+}
+
+int
+guestfs__set_network (guestfs_h *g, int v)
+{
+ g->enable_network = !!v;
+ return 0;
+}
+
+int
+guestfs__get_network (guestfs_h *g)
+{
+ return g->enable_network;
+}
+
+static int
+parse_attach_method (guestfs_h *g, const char *method)
+{
+ if (STREQ (method, "appliance")) {
+ g->attach_method = ATTACH_METHOD_APPLIANCE;
+ free (g->attach_method_arg);
+ g->attach_method_arg = NULL;
+ return 0;
+ }
+
+ if (STREQ (method, "libvirt")) {
+ g->attach_method = ATTACH_METHOD_LIBVIRT;
+ free (g->attach_method_arg);
+ g->attach_method_arg = NULL;
+ return 0;
+ }
+
+ if (STRPREFIX (method, "libvirt:") && strlen (method) > 8) {
+ g->attach_method = ATTACH_METHOD_LIBVIRT;
+ free (g->attach_method_arg);
+ g->attach_method_arg = safe_strdup (g, method + 8);
+ return 0;
+ }
+
+ if (STRPREFIX (method, "unix:") && strlen (method) > 5) {
+ g->attach_method = ATTACH_METHOD_UNIX;
+ free (g->attach_method_arg);
+ g->attach_method_arg = safe_strdup (g, method + 5);
+ /* Note that we don't check the path exists until launch is called. */
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+guestfs__set_attach_method (guestfs_h *g, const char *method)
+{
+ if (parse_attach_method (g, method) == -1) {
+ error (g, "invalid attach method: %s", method);
+ return -1;
+ }
+
+ return 0;
+}
+
+char *
+guestfs__get_attach_method (guestfs_h *g)
+{
+ char *ret;
+
+ switch (g->attach_method) {
+ case ATTACH_METHOD_APPLIANCE:
+ ret = safe_strdup (g, "appliance");
+ break;
+
+ case ATTACH_METHOD_LIBVIRT:
+ if (g->attach_method_arg == NULL)
+ ret = safe_strdup (g, "libvirt");
+ else
+ ret = safe_asprintf (g, "libvirt:%s", g->attach_method_arg);
+ break;
+
+ case ATTACH_METHOD_UNIX:
+ ret = safe_asprintf (g, "unix:%s", g->attach_method_arg);
+ break;
+
+ default: /* keep GCC happy - this is not reached */
+ abort ();
+ }
+
+ return ret;
+}
+
+int
+guestfs__set_pgroup (guestfs_h *g, int v)
+{
+ g->pgroup = !!v;
+ return 0;
+}
+
+int
+guestfs__get_pgroup (guestfs_h *g)
+{
+ return g->pgroup;
+}
+
+int
+guestfs__set_smp (guestfs_h *g, int v)
+{
+ if (v > 255) {
+ error (g, "unsupported number of smp vcpus: %d", v);
+ return -1;
+ } else if (v >= 1) {
+ g->smp = v;
+ return 0;
+ } else {
+ error (g, "invalid smp parameter: %d", v);
+ return -1;
+ }
+}
+
+int
+guestfs__get_smp (guestfs_h *g)
+{
+ return g->smp;
+}