diff options
Diffstat (limited to 'src/interface/interface_backend_netcf.c')
-rw-r--r-- | src/interface/interface_backend_netcf.c | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/src/interface/interface_backend_netcf.c b/src/interface/interface_backend_netcf.c new file mode 100644 index 000000000..3b4ee1144 --- /dev/null +++ b/src/interface/interface_backend_netcf.c @@ -0,0 +1,811 @@ +/* + * interface_driver.c: backend driver methods to handle physical + * interface configuration using the netcf library. + * + * Copyright (C) 2006-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 + * <http://www.gnu.org/licenses/>. + * + * Author: Laine Stump <laine@redhat.com> + */ + +#include <config.h> + +#include <netcf.h> + +#include "virterror_internal.h" +#include "datatypes.h" +#include "interface_driver.h" +#include "interface_conf.h" +#include "memory.h" +#include "logging.h" + +#define VIR_FROM_THIS VIR_FROM_INTERFACE + +/* Main driver state */ +struct interface_driver +{ + virMutex lock; + struct netcf *netcf; +}; + + +static void interfaceDriverLock(struct interface_driver *driver) +{ + virMutexLock(&driver->lock); +} + +static void interfaceDriverUnlock(struct interface_driver *driver) +{ + virMutexUnlock(&driver->lock); +} + +static int netcf_to_vir_err(int netcf_errcode) +{ + switch (netcf_errcode) + { + case NETCF_NOERROR: + /* no error, everything ok */ + return VIR_ERR_OK; + case NETCF_EINTERNAL: + /* internal error, aka bug */ + return VIR_ERR_INTERNAL_ERROR; + case NETCF_EOTHER: + /* other error, copout for being more specific */ + return VIR_ERR_INTERNAL_ERROR; + case NETCF_ENOMEM: + /* + * allocation failed return VIR ERR NO MEMORY + * though it should not be used now. + */ + return 2; + case NETCF_EXMLPARSER: + /* XML parser choked */ + return VIR_ERR_XML_ERROR; + case NETCF_EXMLINVALID: + /* XML invalid in some form */ + return VIR_ERR_XML_ERROR; + case NETCF_ENOENT: + /* Required entry in a tree is missing */ + return VIR_ERR_INTERNAL_ERROR; + case NETCF_EEXEC: + /* external program execution failed or returned non-0 */ + return VIR_ERR_INTERNAL_ERROR; +#ifdef NETCF_EINVALIDOP + case NETCF_EINVALIDOP: + /* attempted operation is invalid while the system is in the current state. */ + return VIR_ERR_OPERATION_INVALID; +#endif + default: + return VIR_ERR_INTERNAL_ERROR; + } +} + +static struct netcf_if *interfaceDriverGetNetcfIF(struct netcf *ncf, virInterfacePtr ifinfo) +{ + /* 1) caller already has lock, + * 2) caller cleans up iface on return + */ + struct netcf_if *iface = ncf_lookup_by_name(ncf, ifinfo->name); + if (!iface) { + const char *errmsg, *details; + int errcode = ncf_error(ncf, &errmsg, &details); + if (errcode != NETCF_NOERROR) { + virReportError(netcf_to_vir_err(errcode), + _("couldn't find interface named '%s': %s%s%s"), + ifinfo->name, errmsg, details ? " - " : "", + details ? details : ""); + } else { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface named '%s'"), + ifinfo->name); + } + } + return iface; +} + +static virDrvOpenStatus interfaceOpenInterface(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + unsigned int flags) +{ + struct interface_driver *driverState; + + virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); + + if (VIR_ALLOC(driverState) < 0) + { + virReportOOMError(); + goto alloc_error; + } + + /* initialize non-0 stuff in driverState */ + if (virMutexInit(&driverState->lock) < 0) + { + /* what error to report? */ + goto mutex_error; + } + + /* open netcf */ + if (ncf_init(&driverState->netcf, NULL) != 0) + { + /* what error to report? */ + goto netcf_error; + } + + conn->interfacePrivateData = driverState; + return VIR_DRV_OPEN_SUCCESS; + +netcf_error: + if (driverState->netcf) + { + ncf_close(driverState->netcf); + } + virMutexDestroy (&driverState->lock); +mutex_error: + VIR_FREE(driverState); +alloc_error: + return VIR_DRV_OPEN_ERROR; +} + +static int interfaceCloseInterface(virConnectPtr conn) +{ + + if (conn->interfacePrivateData != NULL) + { + struct interface_driver *driver = conn->interfacePrivateData; + + /* close netcf instance */ + ncf_close(driver->netcf); + /* destroy lock */ + virMutexDestroy(&driver->lock); + /* free driver state */ + VIR_FREE(driver); + } + conn->interfacePrivateData = NULL; + return 0; +} + +static int interfaceNumOfInterfaces(virConnectPtr conn) +{ + int count; + struct interface_driver *driver = conn->interfacePrivateData; + + interfaceDriverLock(driver); + count = ncf_num_of_interfaces(driver->netcf, NETCF_IFACE_ACTIVE); + if (count < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to get number of interfaces on host: %s%s%s"), + errmsg, details ? " - " : "", details ? details : ""); + } + + interfaceDriverUnlock(driver); + return count; +} + +static int interfaceListInterfaces(virConnectPtr conn, char **const names, int nnames) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int count; + + interfaceDriverLock(driver); + + count = ncf_list_interfaces(driver->netcf, nnames, names, NETCF_IFACE_ACTIVE); + if (count < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to list host interfaces: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + } + + interfaceDriverUnlock(driver); + return count; + +} + +static int interfaceNumOfDefinedInterfaces(virConnectPtr conn) +{ + int count; + struct interface_driver *driver = conn->interfacePrivateData; + + interfaceDriverLock(driver); + count = ncf_num_of_interfaces(driver->netcf, NETCF_IFACE_INACTIVE); + if (count < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to get number of defined interfaces on host: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + } + + interfaceDriverUnlock(driver); + return count; +} + +static int interfaceListDefinedInterfaces(virConnectPtr conn, char **const names, int nnames) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int count; + + interfaceDriverLock(driver); + + count = ncf_list_interfaces(driver->netcf, nnames, names, NETCF_IFACE_INACTIVE); + if (count < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to list host defined interfaces: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + } + + interfaceDriverUnlock(driver); + return count; + +} + +static int +interfaceListAllInterfaces(virConnectPtr conn, + virInterfacePtr **ifaces, + unsigned int flags) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int count; + int i; + struct netcf_if *iface = NULL; + virInterfacePtr *tmp_iface_objs = NULL; + virInterfacePtr iface_obj = NULL; + unsigned int status; + int niface_objs = 0; + int ret = -1; + char **names = NULL; + + virCheckFlags(VIR_CONNECT_LIST_INTERFACES_ACTIVE | + VIR_CONNECT_LIST_INTERFACES_INACTIVE, -1); + + interfaceDriverLock(driver); + + /* List all interfaces, in case of we might support new filter flags + * except active|inactive in future. + */ + count = ncf_num_of_interfaces(driver->netcf, NETCF_IFACE_ACTIVE | + NETCF_IFACE_INACTIVE); + if (count < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to get number of host interfaces: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + ret = -1; + goto cleanup; + } + + if (count == 0) { + ret = 0; + goto cleanup; + } + + if (VIR_ALLOC_N(names, count) < 0) { + virReportOOMError(); + ret = -1; + goto cleanup; + } + + if ((count = ncf_list_interfaces(driver->netcf, count, names, + NETCF_IFACE_ACTIVE | + NETCF_IFACE_INACTIVE)) < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to list host interfaces: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + + if (ifaces) { + if (VIR_ALLOC_N(tmp_iface_objs, count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + } + + for (i = 0; i < count; i++) { + iface = ncf_lookup_by_name(driver->netcf, names[i]); + if (!iface) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + if (errcode != NETCF_NOERROR) { + virReportError(netcf_to_vir_err(errcode), + _("couldn't find interface named '%s': %s%s%s"), + names[i], errmsg, + details ? " - " : "", details ? details : ""); + goto cleanup; + } else { + /* Ignore the NETCF_NOERROR, as the interface is very likely + * deleted by other management apps (e.g. virt-manager). + */ + VIR_WARN("couldn't find interface named '%s', might be " + "deleted by other process", names[i]); + continue; + } + } + + if (ncf_if_status(iface, &status) < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to get status of interface %s: %s%s%s"), + names[i], errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + + /* XXX: Filter the result, need to be splitted once new filter flags + * except active|inactive are supported. + */ + if (((status & NETCF_IFACE_ACTIVE) && + (flags & VIR_CONNECT_LIST_INTERFACES_ACTIVE)) || + ((status & NETCF_IFACE_INACTIVE) && + (flags & VIR_CONNECT_LIST_INTERFACES_INACTIVE))) { + if (ifaces) { + iface_obj = virGetInterface(conn, ncf_if_name(iface), + ncf_if_mac_string(iface)); + tmp_iface_objs[niface_objs] = iface_obj; + } + niface_objs++; + } + + ncf_if_free(iface); + iface = NULL; + } + + if (tmp_iface_objs) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(tmp_iface_objs, niface_objs + 1)); + *ifaces = tmp_iface_objs; + tmp_iface_objs = NULL; + } + + ret = niface_objs; + +cleanup: + ncf_if_free(iface); + + if (names) + for (i = 0; i < count; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (tmp_iface_objs) { + for (i = 0; i < niface_objs; i++) { + if (tmp_iface_objs[i]) + virInterfaceFree(tmp_iface_objs[i]); + } + } + + interfaceDriverUnlock(driver); + return ret; +} + + +static virInterfacePtr interfaceLookupByName(virConnectPtr conn, + const char *name) +{ + struct interface_driver *driver = conn->interfacePrivateData; + struct netcf_if *iface; + virInterfacePtr ret = NULL; + + interfaceDriverLock(driver); + iface = ncf_lookup_by_name(driver->netcf, name); + if (!iface) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + if (errcode != NETCF_NOERROR) { + virReportError(netcf_to_vir_err(errcode), + _("couldn't find interface named '%s': %s%s%s"), + name, errmsg, + details ? " - " : "", details ? details : ""); + } else { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface named '%s'"), name); + } + goto cleanup; + } + + ret = virGetInterface(conn, ncf_if_name(iface), ncf_if_mac_string(iface)); + +cleanup: + ncf_if_free(iface); + interfaceDriverUnlock(driver); + return ret; +} + +static virInterfacePtr interfaceLookupByMACString(virConnectPtr conn, + const char *macstr) +{ + struct interface_driver *driver = conn->interfacePrivateData; + struct netcf_if *iface; + int niface; + virInterfacePtr ret = NULL; + + interfaceDriverLock(driver); + niface = ncf_lookup_by_mac_string(driver->netcf, macstr, 1, &iface); + + if (niface < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("couldn't find interface with MAC address '%s': %s%s%s"), + macstr, errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + if (niface == 0) { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface with MAC address '%s'"), + macstr); + goto cleanup; + } + if (niface > 1) { + virReportError(VIR_ERR_MULTIPLE_INTERFACES, + "%s", _("multiple interfaces with matching MAC address")); + goto cleanup; + } + + ret = virGetInterface(conn, ncf_if_name(iface), ncf_if_mac_string(iface)); + +cleanup: + ncf_if_free(iface); + interfaceDriverUnlock(driver); + return ret; +} + +static char *interfaceGetXMLDesc(virInterfacePtr ifinfo, + unsigned int flags) +{ + struct interface_driver *driver = ifinfo->conn->interfacePrivateData; + struct netcf_if *iface = NULL; + char *xmlstr = NULL; + virInterfaceDefPtr ifacedef = NULL; + char *ret = NULL; + + virCheckFlags(VIR_INTERFACE_XML_INACTIVE, NULL); + + interfaceDriverLock(driver); + + iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo); + if (!iface) { + /* helper already reported error */ + goto cleanup; + } + + if ((flags & VIR_INTERFACE_XML_INACTIVE)) { + xmlstr = ncf_if_xml_desc(iface); + } else { + xmlstr = ncf_if_xml_state(iface); + } + if (!xmlstr) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("could not get interface XML description: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + + ifacedef = virInterfaceDefParseString(xmlstr); + if (!ifacedef) { + /* error was already reported */ + goto cleanup; + } + + ret = virInterfaceDefFormat(ifacedef); + if (!ret) { + /* error was already reported */ + goto cleanup; + } + +cleanup: + ncf_if_free(iface); + VIR_FREE(xmlstr); + virInterfaceDefFree(ifacedef); + interfaceDriverUnlock(driver); + return ret; +} + +static virInterfacePtr interfaceDefineXML(virConnectPtr conn, + const char *xml, + unsigned int flags) +{ + struct interface_driver *driver = conn->interfacePrivateData; + struct netcf_if *iface = NULL; + char *xmlstr = NULL; + virInterfaceDefPtr ifacedef = NULL; + virInterfacePtr ret = NULL; + + virCheckFlags(0, NULL); + + interfaceDriverLock(driver); + + ifacedef = virInterfaceDefParseString(xml); + if (!ifacedef) { + /* error was already reported */ + goto cleanup; + } + + xmlstr = virInterfaceDefFormat(ifacedef); + if (!xmlstr) { + /* error was already reported */ + goto cleanup; + } + + iface = ncf_define(driver->netcf, xmlstr); + if (!iface) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("could not get interface XML description: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + + ret = virGetInterface(conn, ncf_if_name(iface), ncf_if_mac_string(iface)); + +cleanup: + ncf_if_free(iface); + VIR_FREE(xmlstr); + virInterfaceDefFree(ifacedef); + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceUndefine(virInterfacePtr ifinfo) { + struct interface_driver *driver = ifinfo->conn->interfacePrivateData; + struct netcf_if *iface = NULL; + int ret = -1; + + interfaceDriverLock(driver); + + iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo); + if (!iface) { + /* helper already reported error */ + goto cleanup; + } + + ret = ncf_if_undefine(iface); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to undefine interface %s: %s%s%s"), + ifinfo->name, errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + +cleanup: + ncf_if_free(iface); + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceCreate(virInterfacePtr ifinfo, + unsigned int flags) +{ + struct interface_driver *driver = ifinfo->conn->interfacePrivateData; + struct netcf_if *iface = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + interfaceDriverLock(driver); + + iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo); + if (!iface) { + /* helper already reported error */ + goto cleanup; + } + + ret = ncf_if_up(iface); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to create (start) interface %s: %s%s%s"), + ifinfo->name, errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + +cleanup: + ncf_if_free(iface); + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceDestroy(virInterfacePtr ifinfo, + unsigned int flags) +{ + struct interface_driver *driver = ifinfo->conn->interfacePrivateData; + struct netcf_if *iface = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + interfaceDriverLock(driver); + + iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo); + if (!iface) { + /* helper already reported error */ + goto cleanup; + } + + ret = ncf_if_down(iface); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to destroy (stop) interface %s: %s%s%s"), + ifinfo->name, errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + +cleanup: + ncf_if_free(iface); + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceIsActive(virInterfacePtr ifinfo) +{ + struct interface_driver *driver = ifinfo->conn->interfacePrivateData; + struct netcf_if *iface = NULL; + unsigned int flags = 0; + int ret = -1; + + interfaceDriverLock(driver); + + iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo); + if (!iface) { + /* helper already reported error */ + goto cleanup; + } + + if (ncf_if_status(iface, &flags) < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to get status of interface %s: %s%s%s"), + ifinfo->name, errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + + ret = flags & NETCF_IFACE_ACTIVE ? 1 : 0; + +cleanup: + ncf_if_free(iface); + interfaceDriverUnlock(driver); + return ret; +} + +#ifdef HAVE_NETCF_TRANSACTIONS +static int interfaceChangeBegin(virConnectPtr conn, unsigned int flags) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_begin(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceChangeCommit(virConnectPtr conn, unsigned int flags) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_commit(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to commit transaction: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceChangeRollback(virConnectPtr conn, unsigned int flags) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_rollback(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to rollback transaction: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} +#endif /* HAVE_NETCF_TRANSACTIONS */ + +static virInterfaceDriver interfaceDriver = { + "Interface", + .open = interfaceOpenInterface, /* 0.7.0 */ + .close = interfaceCloseInterface, /* 0.7.0 */ + .numOfInterfaces = interfaceNumOfInterfaces, /* 0.7.0 */ + .listInterfaces = interfaceListInterfaces, /* 0.7.0 */ + .numOfDefinedInterfaces = interfaceNumOfDefinedInterfaces, /* 0.7.0 */ + .listDefinedInterfaces = interfaceListDefinedInterfaces, /* 0.7.0 */ + .listAllInterfaces = interfaceListAllInterfaces, /* 0.10.2 */ + .interfaceLookupByName = interfaceLookupByName, /* 0.7.0 */ + .interfaceLookupByMACString = interfaceLookupByMACString, /* 0.7.0 */ + .interfaceGetXMLDesc = interfaceGetXMLDesc, /* 0.7.0 */ + .interfaceDefineXML = interfaceDefineXML, /* 0.7.0 */ + .interfaceUndefine = interfaceUndefine, /* 0.7.0 */ + .interfaceCreate = interfaceCreate, /* 0.7.0 */ + .interfaceDestroy = interfaceDestroy, /* 0.7.0 */ + .interfaceIsActive = interfaceIsActive, /* 0.7.3 */ +#ifdef HAVE_NETCF_TRANSACTIONS + .interfaceChangeBegin = interfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = interfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = interfaceChangeRollback, /* 0.9.2 */ +#endif /* HAVE_NETCF_TRANSACTIONS */ +}; + +int interfaceRegister(void) { + virRegisterInterfaceDriver(&interfaceDriver); + return 0; +} |