/* * libvirt-gobject-connection.c: libvirt glib integration * * Copyright (C) 2008 Daniel P. Berrange * Copyright (C) 2010-2011 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.1 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, see * . * * Author: Daniel P. Berrange */ #include #include #include #include #include "libvirt-glib/libvirt-glib.h" #include "libvirt-gobject/libvirt-gobject.h" #include "libvirt-gobject-compat.h" #define GVIR_CONNECTION_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), GVIR_TYPE_CONNECTION, GVirConnectionPrivate)) struct _GVirConnectionPrivate { GMutex *lock; gchar *uri; virConnectPtr conn; gboolean read_only; GHashTable *domains; GHashTable *pools; GHashTable *interfaces; GHashTable *networks; }; G_DEFINE_TYPE_WITH_PRIVATE(GVirConnection, gvir_connection, G_TYPE_OBJECT); enum { PROP_0, PROP_URI, PROP_HANDLE, }; enum { VIR_CONNECTION_OPENED, VIR_CONNECTION_CLOSED, VIR_DOMAIN_ADDED, VIR_DOMAIN_REMOVED, LAST_SIGNAL }; static gint signals[LAST_SIGNAL]; #define GVIR_CONNECTION_ERROR gvir_connection_error_quark() static GQuark gvir_connection_error_quark(void) { return g_quark_from_static_string("gvir-connection"); } static GVirNodeInfo * gvir_node_info_copy(GVirNodeInfo *info) { return g_slice_dup(GVirNodeInfo, info); } static void gvir_node_info_free(GVirNodeInfo *info) { g_slice_free(GVirNodeInfo, info); } G_DEFINE_BOXED_TYPE(GVirNodeInfo, gvir_node_info, gvir_node_info_copy, gvir_node_info_free) static void gvir_connection_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GVirConnection *conn = GVIR_CONNECTION(object); GVirConnectionPrivate *priv = conn->priv; switch (prop_id) { case PROP_URI: g_value_set_string(value, priv->uri); break; case PROP_HANDLE: g_value_set_boxed(value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); } } static void gvir_connection_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GVirConnection *conn = GVIR_CONNECTION(object); GVirConnectionPrivate *priv = conn->priv; switch (prop_id) { case PROP_URI: g_free(priv->uri); priv->uri = g_value_dup_string(value); break; case PROP_HANDLE: if (priv->conn) virConnectClose(priv->conn); priv->conn = g_value_dup_boxed(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); } } static void gvir_connection_finalize(GObject *object) { GVirConnection *conn = GVIR_CONNECTION(object); GVirConnectionPrivate *priv = conn->priv; if (gvir_connection_is_open(conn)) gvir_connection_close(conn); g_mutex_free(priv->lock); g_free(priv->uri); G_OBJECT_CLASS(gvir_connection_parent_class)->finalize(object); } static GVirStream* gvir_connection_stream_new(GVirConnection *self G_GNUC_UNUSED, gpointer handle) { return g_object_new(GVIR_TYPE_STREAM, "handle", handle, NULL); } static void gvir_connection_class_init(GVirConnectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); gvir_event_register(); object_class->finalize = gvir_connection_finalize; object_class->get_property = gvir_connection_get_property; object_class->set_property = gvir_connection_set_property; klass->stream_new = gvir_connection_stream_new; g_object_class_install_property(object_class, PROP_URI, g_param_spec_string("uri", "URI", "The connection URI", NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_HANDLE, g_param_spec_boxed("handle", "Handle", "The connection handle", GVIR_TYPE_CONNECTION_HANDLE, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); signals[VIR_CONNECTION_OPENED] = g_signal_new("connection-opened", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GVirConnectionClass, connection_opened), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[VIR_CONNECTION_CLOSED] = g_signal_new("connection-closed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GVirConnectionClass, connection_closed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[VIR_DOMAIN_ADDED] = g_signal_new("domain-added", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GVirConnectionClass, domain_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GVIR_TYPE_DOMAIN); signals[VIR_DOMAIN_REMOVED] = g_signal_new("domain-removed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GVirConnectionClass, domain_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GVIR_TYPE_DOMAIN); } static void gvir_connection_init(GVirConnection *conn) { GVirConnectionPrivate *priv; priv = conn->priv = GVIR_CONNECTION_GET_PRIVATE(conn); priv->lock = g_mutex_new(); priv->domains = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); priv->pools = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); priv->interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); priv->networks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); } GVirConnection *gvir_connection_new(const char *uri) { return GVIR_CONNECTION(g_object_new(GVIR_TYPE_CONNECTION, "uri", uri, NULL)); } static int domain_event_cb(virConnectPtr conn G_GNUC_UNUSED, virDomainPtr dom, int event, int detail, void *opaque) { gchar uuid[VIR_UUID_STRING_BUFLEN]; GHashTable *doms; GVirConnection *gconn = opaque; GVirDomain *gdom; GVirConnectionPrivate *priv = gconn->priv; if (virDomainGetUUIDString(dom, uuid) < 0) { gvir_warning("Failed to get domain UUID on %p", dom); return 0; } g_debug("%s: %s event:%d, detail:%d", G_STRFUNC, uuid, event, detail); g_mutex_lock(priv->lock); doms = g_hash_table_ref(priv->domains); gdom = g_hash_table_lookup(doms, uuid); if (gdom != NULL) g_object_ref(G_OBJECT(gdom)); g_mutex_unlock(priv->lock); if (gdom == NULL) { gdom = GVIR_DOMAIN(g_object_new(GVIR_TYPE_DOMAIN, "handle", dom, NULL)); g_mutex_lock(priv->lock); g_hash_table_insert(doms, (gpointer)gvir_domain_get_uuid(gdom), g_object_ref(G_OBJECT(gdom))); g_mutex_unlock(priv->lock); } switch (event) { case VIR_DOMAIN_EVENT_DEFINED: if (detail == VIR_DOMAIN_EVENT_DEFINED_ADDED) g_signal_emit(gconn, signals[VIR_DOMAIN_ADDED], 0, gdom); else if (detail == VIR_DOMAIN_EVENT_DEFINED_UPDATED) g_signal_emit_by_name(gdom, "updated"); else g_warn_if_reached(); break; case VIR_DOMAIN_EVENT_UNDEFINED: if (detail == VIR_DOMAIN_EVENT_UNDEFINED_REMOVED) { g_mutex_lock(priv->lock); g_hash_table_remove(doms, uuid); g_mutex_unlock(priv->lock); g_signal_emit(gconn, signals[VIR_DOMAIN_REMOVED], 0, gdom); } else g_warn_if_reached(); break; case VIR_DOMAIN_EVENT_STARTED: if (detail == VIR_DOMAIN_EVENT_STARTED_BOOTED) { if (!virDomainIsPersistent(dom)) g_signal_emit(gconn, signals[VIR_DOMAIN_ADDED], 0, gdom); g_signal_emit_by_name(gdom, "started::booted"); } else if (detail == VIR_DOMAIN_EVENT_STARTED_MIGRATED) g_signal_emit_by_name(gdom, "started::migrated"); else if (detail == VIR_DOMAIN_EVENT_STARTED_RESTORED) g_signal_emit_by_name(gdom, "started::restored"); else if (detail == VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT) g_signal_emit_by_name(gdom, "started::from-snapshot"); else if (detail == VIR_DOMAIN_EVENT_STARTED_WAKEUP) g_signal_emit_by_name(gdom, "started::wakeup"); else g_warn_if_reached(); break; case VIR_DOMAIN_EVENT_SUSPENDED: if (detail == VIR_DOMAIN_EVENT_SUSPENDED_PAUSED) g_signal_emit_by_name(gdom, "suspended::paused"); else if (detail == VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED) g_signal_emit_by_name(gdom, "suspended::migrated"); else if (detail == VIR_DOMAIN_EVENT_SUSPENDED_IOERROR) g_signal_emit_by_name(gdom, "suspended::ioerror"); else if (detail == VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG) g_signal_emit_by_name(gdom, "suspended::watchdog"); else if (detail == VIR_DOMAIN_EVENT_SUSPENDED_RESTORED) g_signal_emit_by_name(gdom, "suspended::restored"); else if (detail == VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT) g_signal_emit_by_name(gdom, "suspended::from-snapshot"); else g_warn_if_reached(); break; case VIR_DOMAIN_EVENT_RESUMED: if (detail == VIR_DOMAIN_EVENT_RESUMED_UNPAUSED) g_signal_emit_by_name(gdom, "resumed::unpaused"); else if (detail == VIR_DOMAIN_EVENT_RESUMED_MIGRATED) g_signal_emit_by_name(gdom, "resumed::migrated"); else if (detail == VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT) g_signal_emit_by_name(gdom, "resumed::from-snapshot"); else g_warn_if_reached(); break; case VIR_DOMAIN_EVENT_STOPPED: if (detail == VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) g_signal_emit_by_name(gdom, "stopped::shutdown"); else if (detail == VIR_DOMAIN_EVENT_STOPPED_DESTROYED) g_signal_emit_by_name(gdom, "stopped::destroyed"); else if (detail == VIR_DOMAIN_EVENT_STOPPED_CRASHED) g_signal_emit_by_name(gdom, "stopped::crashed"); else if (detail == VIR_DOMAIN_EVENT_STOPPED_MIGRATED) g_signal_emit_by_name(gdom, "stopped::migrated"); else if (detail == VIR_DOMAIN_EVENT_STOPPED_SAVED) g_signal_emit_by_name(gdom, "stopped::saved"); else if (detail == VIR_DOMAIN_EVENT_STOPPED_FAILED) g_signal_emit_by_name(gdom, "stopped::failed"); else if (detail == VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT) g_signal_emit_by_name(gdom, "stopped::from-snapshot"); else g_warn_if_reached(); if (virDomainIsPersistent(dom) != 1) { g_mutex_lock(priv->lock); g_hash_table_remove(doms, uuid); g_mutex_unlock(priv->lock); g_signal_emit(gconn, signals[VIR_DOMAIN_REMOVED], 0, gdom); } break; case VIR_DOMAIN_EVENT_SHUTDOWN: break; case VIR_DOMAIN_EVENT_PMSUSPENDED: if (detail == VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY) g_signal_emit_by_name(gdom, "pmsuspended::memory"); else g_warn_if_reached(); break; default: g_warn_if_reached(); } g_object_unref(G_OBJECT(gdom)); g_hash_table_unref(doms); return 0; } static gboolean _gvir_connection_open(GVirConnection *conn, gboolean read_only, GCancellable *cancellable, GError **err) { GVirConnectionPrivate *priv; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable), FALSE); g_return_val_if_fail((err == NULL) || (*err == NULL), FALSE); priv = conn->priv; if (g_cancellable_set_error_if_cancelled(cancellable, err)) return FALSE; g_mutex_lock(priv->lock); if (priv->conn) { g_set_error(err, GVIR_CONNECTION_ERROR, 0, _("Connection %s is already open"), priv->uri); g_mutex_unlock(priv->lock); return FALSE; } if (read_only) { priv->conn = virConnectOpenReadOnly(priv->uri); priv->read_only = TRUE; } else { priv->conn = virConnectOpen(priv->uri); } if (!priv->conn) { gvir_set_error(err, GVIR_CONNECTION_ERROR, 0, _("Unable to open %s"), priv->uri); g_mutex_unlock(priv->lock); return FALSE; } if (!priv->uri) { char *uri = virConnectGetURI(priv->conn); if (!uri) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Unable to get connection URI")); virConnectClose(priv->conn); priv->conn = NULL; g_mutex_unlock(priv->lock); return FALSE; } priv->uri = g_strdup(uri); free(uri); } if (virConnectDomainEventRegister(priv->conn, domain_event_cb, conn, NULL) == -1) gvir_warning("Failed to register domain events, ignoring"); g_mutex_unlock(priv->lock); g_signal_emit(conn, signals[VIR_CONNECTION_OPENED], 0); return TRUE; } /** * gvir_connection_open: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object */ gboolean gvir_connection_open(GVirConnection *conn, GCancellable *cancellable, GError **err) { return _gvir_connection_open(conn, FALSE, cancellable, err); } gboolean gvir_connection_open_read_only(GVirConnection *conn, GCancellable *cancellable, GError **err) { return _gvir_connection_open(conn, TRUE, cancellable, err); } static void gvir_connection_open_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable) { GVirConnection *conn = GVIR_CONNECTION(object); GError *err = NULL; if (!gvir_connection_open(conn, cancellable, &err)) g_task_return_error(task, err); else g_task_return_boolean(task, TRUE); } /** * gvir_connection_open_async: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_open_async(GVirConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_open_async); g_task_run_in_thread(task, gvir_connection_open_helper); g_object_unref(task); } /** * gvir_connection_open_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result */ gboolean gvir_connection_open_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), FALSE); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_open_async, FALSE); return g_task_propagate_boolean(G_TASK(result), err); } static void gvir_connection_open_read_only_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable) { GVirConnection *conn = GVIR_CONNECTION(object); GError *err = NULL; if (!gvir_connection_open_read_only(conn, cancellable, &err)) g_task_return_error(task, err); else g_task_return_boolean(task, TRUE); } /** * gvir_connection_open_read_only_async: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_open_read_only_async(GVirConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_open_read_only_async); g_task_run_in_thread(task, gvir_connection_open_read_only_helper); g_object_unref(task); } /** * gvir_connection_open_read_only_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result */ gboolean gvir_connection_open_read_only_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), FALSE); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_open_read_only_async, FALSE); return g_task_propagate_boolean(G_TASK(result), err); } gboolean gvir_connection_is_open(GVirConnection *conn) { GVirConnectionPrivate *priv; gboolean open = TRUE; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); priv = conn->priv; g_mutex_lock(priv->lock); if (!priv->conn) open = FALSE; g_mutex_unlock(priv->lock); return open; } gboolean gvir_connection_is_read_only(GVirConnection *conn) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); return conn->priv->read_only; } void gvir_connection_close(GVirConnection *conn) { GVirConnectionPrivate *priv; g_return_if_fail(GVIR_IS_CONNECTION(conn)); priv = conn->priv; g_debug("Close GVirConnection=%p", conn); g_mutex_lock(priv->lock); if (priv->domains) { g_hash_table_unref(priv->domains); priv->domains = NULL; } if (priv->pools) { g_hash_table_unref(priv->pools); priv->pools = NULL; } if (priv->interfaces) { g_hash_table_unref(priv->interfaces); priv->interfaces = NULL; } if (priv->networks) { g_hash_table_unref(priv->networks); priv->networks = NULL; } if (priv->conn) { virConnectDomainEventDeregister(priv->conn, domain_event_cb); virConnectClose(priv->conn); priv->conn = NULL; } /* xxx signals */ g_mutex_unlock(priv->lock); g_signal_emit(conn, signals[VIR_CONNECTION_CLOSED], 0); } /** * gvir_connection_fetch_domains: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * * Use this method to fetch all domains managed by connection * @conn. Use e.g. #gvir_connection_find_domain_by_id or * #gvir_connection_get_domain afterwards to query the fetched * domains. */ gboolean gvir_connection_fetch_domains(GVirConnection *conn, GCancellable *cancellable, GError **err) { GVirConnectionPrivate *priv; GHashTable *doms; virDomainPtr *domains = NULL; gint ndomains = 0; gboolean ret = FALSE; gint i; virConnectPtr vconn = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable), FALSE); g_return_val_if_fail((err == NULL) || (*err == NULL), FALSE); priv = conn->priv; g_mutex_lock(priv->lock); if (!priv->conn) { g_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Connection is not open")); g_mutex_unlock(priv->lock); goto cleanup; } vconn = priv->conn; /* Stop another thread closing the connection just at the minute */ virConnectRef(vconn); g_mutex_unlock(priv->lock); ndomains = virConnectListAllDomains(vconn, &domains, 0); if (ndomains < 0) { gvir_set_error(err, GVIR_CONNECTION_ERROR, 0, _("Failed to fetch list of domains")); goto cleanup; } if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; doms = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); for (i = 0 ; i < ndomains; i++) { GVirDomain *dom; dom = GVIR_DOMAIN(g_object_new(GVIR_TYPE_DOMAIN, "handle", domains[i], NULL)); g_hash_table_insert(doms, (gpointer)gvir_domain_get_uuid(dom), dom); } g_mutex_lock(priv->lock); if (priv->domains) g_hash_table_unref(priv->domains); priv->domains = doms; g_mutex_unlock(priv->lock); ret = TRUE; cleanup: if (ndomains > 0) { for (i = 0 ; i < ndomains; i++) virDomainFree(domains[i]); free(domains); } if (vconn != NULL) virConnectClose(vconn); return ret; } /** * gvir_connection_fetch_storage_pools: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * * Use this method to fetch all storage pools managed by connection * @conn. Use e.g. #gvir_connection_find_storage_pool_by_name or * #gvir_connection_get_storage_pools afterwards to query the fetched * pools. */ gboolean gvir_connection_fetch_storage_pools(GVirConnection *conn, GCancellable *cancellable, GError **err) { GVirConnectionPrivate *priv; GHashTable *pools; virStoragePoolPtr *vpools = NULL; gint npools = 0; gboolean ret = FALSE; gint i; virConnectPtr vconn = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable), FALSE); g_return_val_if_fail((err == NULL) || (*err == NULL), FALSE); priv = conn->priv; g_mutex_lock(priv->lock); if (!priv->conn) { g_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Connection is not open")); g_mutex_unlock(priv->lock); goto cleanup; } vconn = priv->conn; /* Stop another thread closing the connection just at the minute */ virConnectRef(vconn); g_mutex_unlock(priv->lock); if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; npools = virConnectListAllStoragePools(vconn, &vpools, 0); if (npools < 0) { gvir_set_error(err, GVIR_CONNECTION_ERROR, 0, _("Failed to fetch list of pools")); goto cleanup; } if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; pools = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); for (i = 0 ; i < npools; i++) { GVirStoragePool *pool; if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; pool = GVIR_STORAGE_POOL(g_object_new(GVIR_TYPE_STORAGE_POOL, "handle", vpools[i], NULL)); g_hash_table_insert(pools, (gpointer)gvir_storage_pool_get_uuid(pool), pool); } g_mutex_lock(priv->lock); if (priv->pools) g_hash_table_unref(priv->pools); priv->pools = pools; g_mutex_unlock(priv->lock); ret = TRUE; cleanup: if (npools > 0) { for (i = 0 ; i < npools; i++) virStoragePoolFree(vpools[i]); free(vpools); } if (vconn != NULL) virConnectClose(vconn); return ret; } static void gvir_connection_fetch_domains_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable) { GVirConnection *conn = GVIR_CONNECTION(object); GError *err = NULL; if (!gvir_connection_fetch_domains(conn, cancellable, &err)) g_task_return_error(task, err); else g_task_return_boolean(task, TRUE); } /** * gvir_connection_fetch_domains_async: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_fetch_domains_async(GVirConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_fetch_domains_async); g_task_run_in_thread(task, gvir_connection_fetch_domains_helper); g_object_unref(task); } /** * gvir_connection_fetch_domains_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result */ gboolean gvir_connection_fetch_domains_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), FALSE); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_fetch_domains_async, FALSE); return g_task_propagate_boolean(G_TASK(result), err); } static void gvir_connection_fetch_pools_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable) { GVirConnection *conn = GVIR_CONNECTION(object); GError *err = NULL; if (!gvir_connection_fetch_storage_pools(conn, cancellable, &err)) g_task_return_error(task, err); else g_task_return_boolean(task, TRUE); } /** * gvir_connection_fetch_storage_pools_async: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_fetch_storage_pools_async(GVirConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_fetch_storage_pools_async); g_task_run_in_thread(task, gvir_connection_fetch_pools_helper); g_object_unref(task); } /** * gvir_connection_fetch_storage_pools_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result */ gboolean gvir_connection_fetch_storage_pools_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), FALSE); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_fetch_storage_pools_async, FALSE); return g_task_propagate_boolean(G_TASK(result), err); } const gchar *gvir_connection_get_uri(GVirConnection *conn) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); return conn->priv->uri; } /** * gvir_connection_get_hypervisor_name: * @conn: a #GVirConnection * @err: return location for any #GError * * Get name of current hypervisor used. * * Return value: new string that should be freed when no longer needed, * or NULL upon error. */ gchar * gvir_connection_get_hypervisor_name(GVirConnection *conn, GError **err) { GVirConnectionPrivate *priv; gchar *ret = NULL; const char *type; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(err == NULL || *err == NULL, NULL); priv = conn->priv; if (!priv->conn) { g_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Connection is not opened")); goto cleanup; } type = virConnectGetType(priv->conn); if (!type) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Unable to get hypervisor name")); goto cleanup; } ret = g_strdup(type); cleanup: return ret; } /** * gvir_connection_get_version: * @conn: a #GVirConnection * @err: return location for any #GError * * Get version of current hypervisor used. * * Return value: version on success, 0 otherwise and @err set. */ gulong gvir_connection_get_version(GVirConnection *conn, GError **err) { GVirConnectionPrivate *priv; gulong ret = 0; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(err == NULL || *err == NULL, 0); priv = conn->priv; if (!priv->conn) { g_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Connection is not opened")); goto cleanup; } if (virConnectGetVersion(priv->conn, &ret) < 0) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Unable to get hypervisor version")); } cleanup: return ret; } static void gvir_domain_ref(gpointer obj, gpointer ignore G_GNUC_UNUSED) { g_object_ref(obj); } /** * gvir_connection_get_domains: * @conn: a #GVirConnection * * Gets a list of the domains available through @conn. * * Return value: (element-type LibvirtGObject.Domain) (transfer full): List * of #GVirDomain. The returned list should be freed with g_list_free(), * after its elements have been unreffed with g_object_unref(). */ GList *gvir_connection_get_domains(GVirConnection *conn) { GVirConnectionPrivate *priv; GList *domains = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); priv = conn->priv; g_mutex_lock(priv->lock); if (priv->domains != NULL) { domains = g_hash_table_get_values(priv->domains); g_list_foreach(domains, gvir_domain_ref, NULL); } g_mutex_unlock(priv->lock); return domains; } /** * gvir_connection_get_storage_pools: * @conn: a #GVirConnection * * Gets a list of the storage pools available through @conn. * * Return value: (element-type LibvirtGObject.StoragePool) (transfer full): List * of #GVirStoragePool. The returned list should be freed with * g_list_free(), after its elements have been unreffed with * g_object_unref(). */ GList *gvir_connection_get_storage_pools(GVirConnection *conn) { GVirConnectionPrivate *priv; GList *pools = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); priv = conn->priv; g_mutex_lock(priv->lock); if (priv->pools != NULL) { pools = g_hash_table_get_values(priv->pools); g_list_foreach(pools, gvir_domain_ref, NULL); } g_mutex_unlock(priv->lock); return pools; } /** * gvir_connection_get_domain: * @conn: a #GVirConnection * @uuid: uuid string of the requested domain * * Return value: (transfer full): the #GVirDomain, or NULL. The returned * object should be unreffed with g_object_unref() when no longer needed. */ GVirDomain *gvir_connection_get_domain(GVirConnection *conn, const gchar *uuid) { GVirConnectionPrivate *priv; GVirDomain *dom; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(uuid != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); dom = g_hash_table_lookup(priv->domains, uuid); if (dom) g_object_ref(dom); g_mutex_unlock(priv->lock); return dom; } /** * gvir_connection_get_storage_pool: * @conn: a #GVirConnection * @uuid: uuid string of the requested storage pool * * Return value: (transfer full): the #GVirStoragePool, or NULL. The returned * object should be unreffed with g_object_unref() when no longer needed. */ GVirStoragePool *gvir_connection_get_storage_pool(GVirConnection *conn, const gchar *uuid) { GVirConnectionPrivate *priv; GVirStoragePool *pool; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(uuid != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); pool = g_hash_table_lookup(priv->pools, uuid); if (pool) g_object_ref(pool); g_mutex_unlock(priv->lock); return pool; } /** * gvir_connection_find_domain_by_id: * @conn: a #GVirConnection * @id: id of the requested domain * * Return value: (transfer full): the #GVirDomain, or NULL. The returned * object should be unreffed with g_object_unref() when no longer needed. */ GVirDomain *gvir_connection_find_domain_by_id(GVirConnection *conn, gint id) { GVirConnectionPrivate *priv; GHashTableIter iter; gpointer key, value; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); priv = conn->priv; g_mutex_lock(priv->lock); g_hash_table_iter_init(&iter, priv->domains); while (g_hash_table_iter_next(&iter, &key, &value)) { GVirDomain *dom = value; gint thisid = gvir_domain_get_id(dom, NULL); if (thisid == id) { g_object_ref(dom); g_mutex_unlock(priv->lock); return dom; } } g_mutex_unlock(priv->lock); return NULL; } /** * gvir_connection_find_domain_by_name: * @conn: a #GVirConnection * @name: name of the requested domain * * Return value: (transfer full): the #GVirDomain, or NULL. The returned * object should be unreffed with g_object_unref() when no longer needed. */ GVirDomain *gvir_connection_find_domain_by_name(GVirConnection *conn, const gchar *name) { GVirConnectionPrivate *priv; GHashTableIter iter; gpointer key, value; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(name != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); g_hash_table_iter_init(&iter, priv->domains); while (g_hash_table_iter_next(&iter, &key, &value)) { GVirDomain *dom = value; const gchar *thisname = gvir_domain_get_name(dom); if (thisname == NULL) continue; if (strcmp(thisname, name) == 0) { g_object_ref(dom); g_mutex_unlock(priv->lock); return dom; } } g_mutex_unlock(priv->lock); return NULL; } /** * gvir_connection_find_storage_pool_by_name: * @conn: a #GVirConnection * @name: name of the requested storage pool * * Return value: (transfer full): the #GVirStoragePool, or NULL. The returned * object should be unreffed with g_object_unref() when no longer needed. */ GVirStoragePool *gvir_connection_find_storage_pool_by_name(GVirConnection *conn, const gchar *name) { GVirConnectionPrivate *priv; GHashTableIter iter; gpointer key, value; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(name != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); g_hash_table_iter_init(&iter, priv->pools); while (g_hash_table_iter_next(&iter, &key, &value)) { GVirStoragePool *pool = value; const gchar *thisname = gvir_storage_pool_get_name(pool); if (thisname == NULL) continue; if (strcmp(thisname, name) == 0) { g_object_ref(pool); g_mutex_unlock(priv->lock); return pool; } } g_mutex_unlock(priv->lock); return NULL; } typedef struct virConnect GVirConnectionHandle; static GVirConnectionHandle* gvir_connection_handle_copy(GVirConnectionHandle *src) { virConnectRef((virConnectPtr)src); return src; } static void gvir_connection_handle_free(GVirConnectionHandle *src) { virConnectClose((virConnectPtr)src); } G_DEFINE_BOXED_TYPE(GVirConnectionHandle, gvir_connection_handle, gvir_connection_handle_copy, gvir_connection_handle_free) /** * gvir_connection_get_stream: * @conn: a #GVirConnection * @flags: flags to use for the stream * * Return value: (transfer full): a #GVirStream stream, or NULL.The returned * object should be unreffed with g_object_unref() when no longer needed. */ GVirStream *gvir_connection_get_stream(GVirConnection *self, guint flags) { GVirConnectionClass *klass; g_return_val_if_fail(GVIR_IS_CONNECTION(self), NULL); g_return_val_if_fail(self->priv->conn, NULL); klass = GVIR_CONNECTION_GET_CLASS(self); g_return_val_if_fail(klass->stream_new, NULL); virStreamPtr st = virStreamNew(self->priv->conn, flags | VIR_STREAM_NONBLOCK); return klass->stream_new(self, st); } /** * gvir_connection_create_domain: * @conn: a #GVirConnection on which to create the domain * @conf: the configuration for the new domain * * Create the configuration file for a new persistent domain. * The returned domain will initially be in the shutoff state. * * Returns: (transfer full): the newly created domain, or NULL if an error * occurred. The returned object should be unreffed with g_object_unref() * when no longer needed. */ GVirDomain *gvir_connection_create_domain(GVirConnection *conn, GVirConfigDomain *conf, GError **err) { gchar *xml; virDomainPtr handle; GVirConnectionPrivate *priv; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(GVIR_CONFIG_IS_DOMAIN(conf), NULL); g_return_val_if_fail((err == NULL) || (*err == NULL), NULL); xml = gvir_config_object_to_xml(GVIR_CONFIG_OBJECT(conf)); g_return_val_if_fail(xml != NULL, NULL); priv = conn->priv; handle = virDomainDefineXML(priv->conn, xml); g_free(xml); if (!handle) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Failed to create domain")); return NULL; } GVirDomain *domain; domain = GVIR_DOMAIN(g_object_new(GVIR_TYPE_DOMAIN, "handle", handle, NULL)); virDomainFree(handle); g_mutex_lock(priv->lock); g_hash_table_insert(priv->domains, (gpointer)gvir_domain_get_uuid(domain), g_object_ref(domain)); g_mutex_unlock(priv->lock); return domain; } /** * gvir_connection_start_domain: * @conn: a #GVirConnection on which to create the domain * @conf: the configuration for the new domain * * Start a new transient domain without persistent configuration. * The returned domain will initially be running. * * Returns: (transfer full): the newly created domain, or NULL if an error * occurred. The returned object should be unreffed with g_object_unref() * when no longer needed. */ GVirDomain *gvir_connection_start_domain(GVirConnection *conn, GVirConfigDomain *conf, guint flags, GError **err) { gchar *xml; virDomainPtr handle; GVirConnectionPrivate *priv; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(GVIR_CONFIG_IS_DOMAIN(conf), NULL); g_return_val_if_fail((err == NULL) || (*err == NULL), NULL); xml = gvir_config_object_to_xml(GVIR_CONFIG_OBJECT(conf)); g_return_val_if_fail(xml != NULL, NULL); priv = conn->priv; handle = virDomainCreateXML(priv->conn, xml, flags); g_free(xml); if (!handle) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Failed to create domain")); return NULL; } GVirDomain *domain; domain = GVIR_DOMAIN(g_object_new(GVIR_TYPE_DOMAIN, "handle", handle, NULL)); virDomainFree(handle); g_mutex_lock(priv->lock); g_hash_table_insert(priv->domains, (gpointer)gvir_domain_get_uuid(domain), g_object_ref(domain)); g_mutex_unlock(priv->lock); return domain; } /** * gvir_connection_fetch_interfaces: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @err: return location for any errors * * Use this method to fetch information on all network interfaces * managed by connection @conn on host machine. Use * #gvir_connection_get_interfaces or #gvir_connection_get_interface afterwards * to query the fetched interfaces. * * Return value: %TRUE on success, %FALSE otherwise and @err is set. */ gboolean gvir_connection_fetch_interfaces(GVirConnection *conn, GCancellable *cancellable, GError **err) { GVirConnectionPrivate *priv; GHashTable *interfaces; virInterfacePtr *ifaces = NULL; gint ninterfaces = 0; gboolean ret = FALSE; gint i; virConnectPtr vconn = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable), FALSE); g_return_val_if_fail((err == NULL) || (*err == NULL), FALSE); priv = conn->priv; g_mutex_lock(priv->lock); if (!priv->conn) { g_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Connection is not open")); g_mutex_unlock(priv->lock); goto cleanup; } vconn = priv->conn; /* Stop another thread closing the connection just at the minute */ virConnectRef(vconn); g_mutex_unlock(priv->lock); if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; ninterfaces = virConnectListAllInterfaces(vconn, &ifaces, 0); if (ninterfaces < 0) { gvir_set_error(err, GVIR_CONNECTION_ERROR, 0, _("Failed to fetch list of interfaces")); goto cleanup; } if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); for (i = 0 ; i < ninterfaces; i++) { GVirInterface *iface; if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; iface = GVIR_INTERFACE(g_object_new(GVIR_TYPE_INTERFACE, "handle", ifaces[i], NULL)); g_hash_table_insert(interfaces, (gpointer)gvir_interface_get_name(iface), iface); } g_mutex_lock(priv->lock); if (priv->interfaces) g_hash_table_unref(priv->interfaces); priv->interfaces = interfaces; g_mutex_unlock(priv->lock); ret = TRUE; cleanup: if (ninterfaces > 0) { for (i = 0 ; i < ninterfaces; i++) virInterfaceFree(ifaces[i]); free(ifaces); } if (vconn != NULL) virConnectClose(vconn); return ret; } static void gvir_connection_fetch_interfaces_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable) { GVirConnection *conn = GVIR_CONNECTION(object); GError *err = NULL; if (!gvir_connection_fetch_interfaces(conn, cancellable, &err)) g_task_return_error(task, err); else g_task_return_boolean(task, TRUE); } /** * gvir_connection_fetch_interfaces_async: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_fetch_interfaces_async(GVirConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_fetch_interfaces_async); g_task_run_in_thread(task, gvir_connection_fetch_interfaces_helper); g_object_unref(task); } /** * gvir_connection_fetch_interfaces_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result * @err: return location for any errors */ gboolean gvir_connection_fetch_interfaces_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), FALSE); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_fetch_interfaces_async, FALSE); return g_task_propagate_boolean(G_TASK(result), err); } /** * gvir_connection_get_interfaces: * @conn: a #GVirConnection * * Get a list of all the network interfaces managed by connection @conn on * host machine. * * Return value: (element-type LibvirtGObject.Interface) (transfer full): List * of #GVirInterface. The returned list should be freed with g_list_free(), * after its elements have been unreffed with g_object_unref(). */ GList *gvir_connection_get_interfaces(GVirConnection *conn) { GVirConnectionPrivate *priv; GList *interfaces = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); priv = conn->priv; g_mutex_lock(priv->lock); if (priv->interfaces != NULL) { interfaces = g_hash_table_get_values(priv->interfaces); g_list_foreach(interfaces, gvir_domain_ref, NULL); } g_mutex_unlock(priv->lock); return interfaces; } /** * gvir_connection_get_interface: * @conn: a #GVirConnection * @name: interface name to lookup * * Get a particular interface which has name @name. * * Return value: (transfer full): A new reference to a #GVirInterface, or NULL * if no interface exists with name @name. The returned object must be unreffed * using g_object_unref() once used. */ GVirInterface *gvir_connection_get_interface(GVirConnection *conn, const gchar *name) { GVirConnectionPrivate *priv; GVirInterface *iface; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(name != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); iface = g_hash_table_lookup(priv->interfaces, name); if (iface) g_object_ref(iface); g_mutex_unlock(priv->lock); return iface; } /** * gvir_connection_find_interface_by_mac: * @conn: a #GVirConnection * @macaddr: MAC address to lookup * * Get a particular interface which has MAC address @mac. * * Return value: (transfer full): A new reference to a #GVirInterface, or NULL * if no interface exists with MAC address @mac. The returned object must be * unreffed using g_object_unref() once used. */ GVirInterface *gvir_connection_find_interface_by_mac(GVirConnection *conn, const gchar *mac) { GVirConnectionPrivate *priv; GHashTableIter iter; gpointer key, value; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(mac != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); g_hash_table_iter_init(&iter, priv->interfaces); while (g_hash_table_iter_next(&iter, &key, &value)) { GVirInterface *iface = value; const gchar *thismac = gvir_interface_get_mac(iface); if (g_strcmp0(thismac, mac) == 0) { g_object_ref(iface); g_mutex_unlock(priv->lock); return iface; } } g_mutex_unlock(priv->lock); return NULL; } /** * gvir_connection_fetch_networks: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * * Use this method to fetch all networks managed by connection * @conn. Use e.g. #gvir_connection_find_network_by_name or * #gvir_connection_get_networks afterwards to query the fetched * domains. */ gboolean gvir_connection_fetch_networks(GVirConnection *conn, GCancellable *cancellable, GError **err) { GVirConnectionPrivate *priv; GHashTable *networks; virNetworkPtr *vnetworks = NULL; gint nnetworks = 0; gboolean ret = FALSE; gint i; virConnectPtr vconn = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable), FALSE); g_return_val_if_fail((err == NULL) || (*err == NULL), FALSE); priv = conn->priv; g_mutex_lock(priv->lock); if (!priv->conn) { g_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Connection is not open")); g_mutex_unlock(priv->lock); goto cleanup; } vconn = priv->conn; /* Stop another thread closing the connection just at the minute */ virConnectRef(vconn); g_mutex_unlock(priv->lock); if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; nnetworks = virConnectListAllNetworks(vconn, &vnetworks, 0); if (nnetworks < 0) { gvir_set_error(err, GVIR_CONNECTION_ERROR, 0, _("Failed to fetch list of networks")); goto cleanup; } if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; networks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); for (i = 0 ; i < nnetworks; i++) { GVirNetwork *network; if (g_cancellable_set_error_if_cancelled(cancellable, err)) goto cleanup; network = GVIR_NETWORK(g_object_new(GVIR_TYPE_NETWORK, "handle", vnetworks[i], NULL)); g_hash_table_insert(networks, (gpointer)gvir_network_get_uuid(network), network); } g_mutex_lock(priv->lock); if (priv->networks) g_hash_table_unref(priv->networks); priv->networks = networks; g_mutex_unlock(priv->lock); ret = TRUE; cleanup: if (nnetworks > 0) { for (i = 0 ; i < nnetworks; i++) virNetworkFree(vnetworks[i]); free(vnetworks); } if (vconn != NULL) virConnectClose(vconn); return ret; } static void gvir_connection_fetch_networks_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable) { GVirConnection *conn = GVIR_CONNECTION(object); GError *err = NULL; if (!gvir_connection_fetch_networks(conn, cancellable, &err)) g_task_return_error(task, err); else g_task_return_boolean(task, TRUE); } /** * gvir_connection_fetch_networks_async: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_fetch_networks_async(GVirConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_fetch_networks_async); g_task_run_in_thread(task, gvir_connection_fetch_networks_helper); g_object_unref(task); } /** * gvir_connection_fetch_networks_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result * @err: return location for any errors */ gboolean gvir_connection_fetch_networks_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), FALSE); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_fetch_networks_async, FALSE); return g_task_propagate_boolean(G_TASK(result), err); } /** * gvir_connection_get_networks: * @conn: a #GVirConnection * * Get a list of all the network networks available through @conn. * * Return value: (element-type LibvirtGObject.Network) (transfer full): List * of #GVirNetwork. The returned list should be freed with g_list_free(), * after its elements have been unreffed with g_object_unref(). */ GList *gvir_connection_get_networks(GVirConnection *conn) { GVirConnectionPrivate *priv; GList *networks = NULL; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); priv = conn->priv; g_mutex_lock(priv->lock); if (priv->networks != NULL) { networks = g_hash_table_get_values(priv->networks); g_list_foreach(networks, gvir_domain_ref, NULL); } g_mutex_unlock(priv->lock); return networks; } /** * gvir_connection_get_network: * @conn: a #GVirConnection * @uuid: UUID of the network to lookup * * Get a particular network which has UUID @uuid. * * Return value: (transfer full): A new reference to a #GVirNetwork, or NULL if * no network exists with UUID @uuid. The returned object must be unreffed using * g_object_unref() once used. */ GVirNetwork *gvir_connection_get_network(GVirConnection *conn, const gchar *uuid) { GVirConnectionPrivate *priv; GVirNetwork *network; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(uuid != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); network = g_hash_table_lookup(priv->networks, uuid); if (network) g_object_ref(network); g_mutex_unlock(priv->lock); return network; } /** * gvir_connection_find_network_by_name: * @conn: a #GVirConnection * @name: name of the network to search for * * Get a particular network which has name @name. * * Return value: (transfer full): A new reference to a #GVirNetwork, or NULL if * no network exists with name @name. The returned object must be unreffed using * g_object_unref() once used. */ GVirNetwork *gvir_connection_find_network_by_name(GVirConnection *conn, const gchar *name) { GVirConnectionPrivate *priv; GHashTableIter iter; gpointer key, value; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(name != NULL, NULL); priv = conn->priv; g_mutex_lock(priv->lock); g_hash_table_iter_init(&iter, priv->networks); while (g_hash_table_iter_next(&iter, &key, &value)) { GVirNetwork *network = value; const gchar *thisname = gvir_network_get_name(network); if (thisname == NULL) continue; if (strcmp(thisname, name) == 0) { g_object_ref(network); g_mutex_unlock(priv->lock); return network; } } g_mutex_unlock(priv->lock); return NULL; } /** * gvir_connection_create_storage_pool: * @conn: a #GVirConnection on which to create the pool * @conf: the configuration for the new storage pool * @flags: the flags * @err: return location for any #GError * * Returns: (transfer full): the newly created storage pool, or NULL if an * error occurred. The returned list should be freed with g_list_free(), * after its elements have been unreffed with g_object_unref(). */ GVirStoragePool *gvir_connection_create_storage_pool (GVirConnection *conn, GVirConfigStoragePool *conf, guint flags, GError **err) { gchar *xml; virStoragePoolPtr handle; GVirConnectionPrivate *priv; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(GVIR_CONFIG_IS_STORAGE_POOL(conf), NULL); g_return_val_if_fail((err == NULL) || (*err == NULL), NULL); xml = gvir_config_object_to_xml(GVIR_CONFIG_OBJECT(conf)); g_return_val_if_fail(xml != NULL, NULL); priv = conn->priv; handle = virStoragePoolDefineXML(priv->conn, xml, flags); g_free(xml); if (!handle) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, flags, _("Failed to create storage pool")); return NULL; } GVirStoragePool *pool; pool = GVIR_STORAGE_POOL(g_object_new(GVIR_TYPE_STORAGE_POOL, "handle", handle, NULL)); virStoragePoolFree(handle); g_mutex_lock(priv->lock); g_hash_table_insert(priv->pools, (gpointer)gvir_storage_pool_get_uuid(pool), pool); g_mutex_unlock(priv->lock); return g_object_ref(pool); } /** * gvir_connection_get_node_info: * @conn: a #GVirConnection * @err: return location for any #GError * * Returns: (transfer full): the info, or NULL if an error occurred. The * returned object should be unreffed with g_object_unref() when no longer * needed. */ GVirNodeInfo *gvir_connection_get_node_info(GVirConnection *conn, GError **err) { GVirConnectionPrivate *priv; virNodeInfo info; GVirNodeInfo *ret; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail((err == NULL) || (*err == NULL), NULL); priv = conn->priv; if (virNodeGetInfo(priv->conn, &info) < 0) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Unable to get node info")); return NULL; } ret = g_slice_new(GVirNodeInfo); g_strlcpy (ret->model, info.model, sizeof (ret->model)); ret->memory = info.memory; ret->cpus = info.cpus; ret->mhz = info.mhz; ret->nodes = info.nodes; ret->sockets = info.sockets; ret->cores = info.cores; ret->threads = info.threads; return ret; } /** * gvir_connection_get_domain_capabilities: * @conn: a #GVirConnection * @emulatorbin: (allow-none): path to emulator * @arch: (allow-none): domain architecture * @machine: (allow-none): machine type * @virttype: (allow-none): virtualization type * @flags: extra flags; not used yet, so callers should always pass 0 * @err: return location for any #GError * * Return value: (transfer full): a #GVirConfigDomainCapabilities or NULL. * The return object should be unreffed with g_object_unref() when no longer * needed. */ GVirConfigDomainCapabilities * gvir_connection_get_domain_capabilities(GVirConnection *conn, const gchar *emulatorbin, const gchar *arch, const gchar *machine, const gchar *virttype, guint flags, GError **err) { GVirConfigDomainCapabilities *domain_caps; gchar *domain_caps_xml; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(err == NULL || *err == NULL, NULL); g_return_val_if_fail(conn->priv->conn, NULL); domain_caps_xml = virConnectGetDomainCapabilities(conn->priv->conn, emulatorbin, arch, machine, virttype, flags); if (domain_caps_xml == NULL) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Unable to get domain capabilities")); return NULL; } domain_caps = gvir_config_domain_capabilities_new_from_xml(domain_caps_xml, err); free(domain_caps_xml); return domain_caps; } typedef struct { gchar *emulatorbin; gchar *arch; gchar *machine; gchar *virttype; guint flags; } GetDomainCapabilitiesData; static void get_domain_capabilities_data_free(GetDomainCapabilitiesData *data) { g_free(data->emulatorbin); g_free(data->arch); g_free(data->machine); g_free(data->virttype); g_slice_free(GetDomainCapabilitiesData, data); } static void gvir_connection_get_domain_capabilities_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable G_GNUC_UNUSED) { GVirConnection *conn = GVIR_CONNECTION(object); GetDomainCapabilitiesData *data; GError *err = NULL; GVirConfigDomainCapabilities *domain_caps; data = (GetDomainCapabilitiesData *)task_data; domain_caps = gvir_connection_get_domain_capabilities(conn, data->emulatorbin, data->arch, data->machine, data->virttype, data->flags, &err); if (domain_caps == NULL) { g_task_return_error(task, err); return; } g_task_return_pointer(task, domain_caps, g_object_unref); } /** * gvir_connection_get_domain_capabilities_async: * @conn: a #GVirConnection * @emulatorbin: (allow-none): path to emulator * @arch: (allow-none): domain architecture * @machine: (allow-none): machine type * @virttype: (allow-none): virtualization type * @flags: extra flags; not used yet, so callers should always pass 0 * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_get_domain_capabilities_async(GVirConnection *conn, const gchar *emulatorbin, const gchar *arch, const gchar *machine, const gchar *virttype, guint flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GetDomainCapabilitiesData *data; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); data = g_slice_new0(GetDomainCapabilitiesData); data->emulatorbin = g_strdup(emulatorbin); data->arch = g_strdup(arch); data->machine = g_strdup(machine); data->virttype = g_strdup(virttype); data->flags = flags; task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_get_domain_capabilities_async); g_task_set_task_data(task, data, (GDestroyNotify)get_domain_capabilities_data_free); g_task_run_in_thread(task, gvir_connection_get_domain_capabilities_helper); g_object_unref(task); } /** * gvir_connection_get_domain_capabilities_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result * * Return value: (transfer full): a #GVirConfigDomainCapabilities or NULL. * The returned object should be unreffed with g_object_unref() when no * longer needed. */ GVirConfigDomainCapabilities * gvir_connection_get_domain_capabilities_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), NULL); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_get_domain_capabilities_async, NULL); return g_task_propagate_pointer(G_TASK(result), err); } /** * gvir_connection_get_capabilities: * @conn: a #GVirConnection * @err: return location for any #GError * * Return value: (transfer full): a #GVirConfigCapabilities or NULL. The * returned object should be unreffed with g_object_unref() when no longer * needed. */ GVirConfigCapabilities *gvir_connection_get_capabilities(GVirConnection *conn, GError **err) { GVirConfigCapabilities *caps; char *caps_xml; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(err == NULL || *err == NULL, NULL); g_return_val_if_fail(conn->priv->conn, NULL); caps_xml = virConnectGetCapabilities(conn->priv->conn); if (caps_xml == NULL) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Unable to get capabilities")); return NULL; } caps = gvir_config_capabilities_new_from_xml(caps_xml, err); free(caps_xml); return caps; } static void gvir_connection_get_capabilities_helper(GTask *task, gpointer object, gpointer task_data G_GNUC_UNUSED, GCancellable *cancellable G_GNUC_UNUSED) { GVirConnection *conn = GVIR_CONNECTION(object); GError *err = NULL; GVirConfigCapabilities *caps; caps = gvir_connection_get_capabilities(conn, &err); if (caps == NULL) { g_task_return_error(task, err); return; } g_task_return_pointer(task, caps, g_object_unref); } /** * gvir_connection_get_capabilities_async: * @conn: a #GVirConnection * @cancellable: (allow-none)(transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback */ void gvir_connection_get_capabilities_async(GVirConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_get_capabilities_async); g_task_run_in_thread(task, gvir_connection_get_capabilities_helper); g_object_unref(task); } /** * gvir_connection_get_capabilities_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result * * Return value: (transfer full): a #GVirConfigCapabilities or NULL. The * returned object should be unreffed with g_object_unref() when no longer * needed. */ GVirConfigCapabilities * gvir_connection_get_capabilities_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), NULL); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), NULL); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_get_capabilities_async, NULL); return g_task_propagate_pointer(G_TASK(result), err); } /** * gvir_connection_restore_domain_from_file: * @conn: a #GVirConnection * @filename: path to input file * @custom_conf: (allow-none): configuration for domain or NULL * @flags: the flags * * Restores the domain saved with #gvir_domain_save_to_file * * Returns: TRUE on success, FALSE otherwise */ gboolean gvir_connection_restore_domain_from_file(GVirConnection *conn, gchar *filename, GVirConfigDomain *custom_conf, guint flags, GError **err) { GVirConnectionPrivate *priv; int ret; g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail((filename != NULL), FALSE); g_return_val_if_fail((err == NULL) || (*err == NULL), FALSE); priv = conn->priv; if (flags || (custom_conf != NULL)) { gchar *custom_xml = NULL; if (custom_conf != NULL) custom_xml = gvir_config_object_to_xml(GVIR_CONFIG_OBJECT(custom_conf)); ret = virDomainRestoreFlags(priv->conn, filename, custom_xml, flags); g_free (custom_xml); } else { ret = virDomainRestore(priv->conn, filename); } if (ret < 0) { gvir_set_error_literal(err, GVIR_CONNECTION_ERROR, 0, _("Unable to restore domain")); return FALSE; } return TRUE; } typedef struct { gchar *filename; GVirConfigDomain *custom_conf; guint flags; } RestoreDomainFromFileData; static void restore_domain_from_file_data_free(RestoreDomainFromFileData *data) { g_free(data->filename); g_clear_object(&data->custom_conf); g_slice_free(RestoreDomainFromFileData, data); } static void gvir_connection_restore_domain_from_file_helper (GTask *task, gpointer object, gpointer task_data, GCancellable *cancellable G_GNUC_UNUSED) { GVirConnection *conn = GVIR_CONNECTION(object); RestoreDomainFromFileData *data; GError *err = NULL; data = (RestoreDomainFromFileData *)task_data; if (!gvir_connection_restore_domain_from_file(conn, data->filename, data->custom_conf, data->flags, &err)) g_task_return_error(task, err); else g_task_return_boolean(task, TRUE); } /** * gvir_connection_restore_domain_from_file_async: * @conn: a #GVirConnection * @filename: path to input file * @custom_conf: (allow-none): configuration for domain * @flags: the flags * @cancellable: (allow-none) (transfer none): cancellation object * @callback: (scope async): completion callback * @user_data: (closure): opaque data for callback * * Asynchronous variant of #gvir_connection_restore_domain_from_file */ void gvir_connection_restore_domain_from_file_async(GVirConnection *conn, gchar *filename, GVirConfigDomain *custom_conf, guint flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; RestoreDomainFromFileData *data; g_return_if_fail(GVIR_IS_CONNECTION(conn)); g_return_if_fail(filename != NULL); g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); data = g_slice_new0(RestoreDomainFromFileData); data->filename = g_strdup(filename); if (custom_conf != NULL) data->custom_conf = g_object_ref(custom_conf); data->flags = flags; task = g_task_new(G_OBJECT(conn), cancellable, callback, user_data); g_task_set_source_tag(task, gvir_connection_restore_domain_from_file_async); g_task_set_task_data(task, data, (GDestroyNotify)restore_domain_from_file_data_free); g_task_run_in_thread(task, gvir_connection_restore_domain_from_file_helper); g_object_unref(task); } /** * gvir_connection_restore_domain_from_file_finish: * @conn: a #GVirConnection * @result: (transfer none): async method result * @err: Place-holder for possible errors * * Finishes the operation started by #gvir_restore_domain_from_file_async. * * Returns: TRUE if domain was restored successfully, FALSE otherwise. */ gboolean gvir_connection_restore_domain_from_file_finish(GVirConnection *conn, GAsyncResult *result, GError **err) { g_return_val_if_fail(GVIR_IS_CONNECTION(conn), FALSE); g_return_val_if_fail(g_task_is_valid(result, G_OBJECT(conn)), FALSE); g_return_val_if_fail(g_task_get_source_tag(G_TASK(result)) == gvir_connection_restore_domain_from_file_async, FALSE); return g_task_propagate_boolean(G_TASK(result), err); }