mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
Add helper program to create custom leases
Introduce helper program to catch events from dnsmasq and maintain a custom lease file per network. It supports dhcpv4 and dhcpv6. The file is saved as "<interface-name>.status". Each lease contains the following info: <expiry-time (epoch time)> <mac> <iaid> <ip-address> <hostname> <clientid> Example of custom leases file content: [ { "iaid": "1221229", "ip-address": "2001:db8:ca2:2:1::95", "mac-address": "52:54:00:12:a2:6d", "hostname": "Fedora20", "client-id": "00:04:1a:c1:d9:6b:5a:0a:e2:bc:f8:4b:1e:37:2e:38:22:55", "expiry-time": 1393244216 }, { "ip-address": "192.168.150.208", "mac-address": "52:54:00:11:56:b3", "hostname": "Wani-PC", "client-id": "01:52:54:00:11:56:b3", "expiry-time": 1393244248 } ] src/Makefile.am: * Add options to compile the helper program src/network/bridge_driver.c: * Introduce networkDnsmasqLeaseFileNameCustom() * Invoke helper program along with dnsmasq * Delete the .status file when corresponding n/w is destroyed. src/network/leaseshelper.c * Helper program to create the custom lease file
This commit is contained in:
parent
6ef0b03483
commit
baafe668fa
@ -1917,6 +1917,7 @@ exit 0
|
||||
%dir %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/networks/autostart
|
||||
%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/network/
|
||||
%dir %attr(0755, root, root) %{_localstatedir}/lib/libvirt/dnsmasq/
|
||||
%attr(0755, root, root) %{_libexecdir}/libvirt_leaseshelper
|
||||
%endif
|
||||
%if %{with_nwfilter}
|
||||
%dir %attr(0700, root, root) %{_sysconfdir}/libvirt/nwfilter/
|
||||
@ -1992,6 +1993,7 @@ exit 0
|
||||
%ghost %dir %{_localstatedir}/run/libvirt/network/
|
||||
%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/network/
|
||||
%dir %attr(0755, root, root) %{_localstatedir}/lib/libvirt/dnsmasq/
|
||||
%attr(0755, root, root) %{_libexecdir}/libvirt_leaseshelper
|
||||
%{_libdir}/%{name}/connection-driver/libvirt_driver_network.so
|
||||
%endif
|
||||
|
||||
|
@ -76,6 +76,7 @@ src/libxl/libxl_driver.c
|
||||
src/libxl/libxl_conf.c
|
||||
src/network/bridge_driver.c
|
||||
src/network/bridge_driver_linux.c
|
||||
src/network/leaseshelper.c
|
||||
src/node_device/node_device_driver.c
|
||||
src/node_device/node_device_hal.c
|
||||
src/node_device/node_device_linux_sysfs.c
|
||||
|
@ -860,6 +860,9 @@ STORAGE_HELPER_DISK_SOURCES = \
|
||||
UTIL_IO_HELPER_SOURCES = \
|
||||
util/iohelper.c
|
||||
|
||||
NETWORK_LEASES_HELPER_SOURCES = \
|
||||
network/leaseshelper.c
|
||||
|
||||
# Network filters
|
||||
NWFILTER_DRIVER_SOURCES = \
|
||||
nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \
|
||||
@ -2468,6 +2471,25 @@ libvirt_iohelper_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
$(PIE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
if WITH_NETWORK
|
||||
libexec_PROGRAMS += libvirt_leaseshelper
|
||||
libvirt_leaseshelper_SOURCES = $(NETWORK_LEASES_HELPER_SOURCES)
|
||||
libvirt_leaseshelper_LDADD = \
|
||||
libvirt_util.la \
|
||||
../gnulib/lib/libgnu.la
|
||||
if WITH_DTRACE_PROBES
|
||||
libvirt_leaseshelper_LDADD += libvirt_probes.lo
|
||||
endif WITH_DTRACE_PROBES
|
||||
|
||||
libvirt_leaseshelper_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
$(PIE_CFLAGS) \
|
||||
$(NULL)
|
||||
else ! WITH_NETWORK
|
||||
EXTRA_DIST += $(NETWORK_LEASES_HELPER_SOURCES)
|
||||
endif ! WITH_NETWORK
|
||||
|
||||
endif WITH_LIBVIRTD
|
||||
|
||||
if WITH_STORAGE_DISK
|
||||
|
@ -210,6 +210,16 @@ networkDnsmasqLeaseFileNameDefault(const char *netname)
|
||||
networkDnsmasqLeaseFileNameFunc networkDnsmasqLeaseFileName =
|
||||
networkDnsmasqLeaseFileNameDefault;
|
||||
|
||||
static char *
|
||||
networkDnsmasqLeaseFileNameCustom(const char *bridge)
|
||||
{
|
||||
char *leasefile;
|
||||
|
||||
ignore_value(virAsprintf(&leasefile, "%s/%s.status",
|
||||
driverState->dnsmasqStateDir, bridge));
|
||||
return leasefile;
|
||||
}
|
||||
|
||||
static char *
|
||||
networkDnsmasqConfigFileName(const char *netname)
|
||||
{
|
||||
@ -246,6 +256,7 @@ networkRemoveInactive(virNetworkDriverStatePtr driver,
|
||||
virNetworkObjPtr net)
|
||||
{
|
||||
char *leasefile = NULL;
|
||||
char *customleasefile = NULL;
|
||||
char *radvdconfigfile = NULL;
|
||||
char *configfile = NULL;
|
||||
char *radvdpidbase = NULL;
|
||||
@ -264,6 +275,9 @@ networkRemoveInactive(virNetworkDriverStatePtr driver,
|
||||
if (!(leasefile = networkDnsmasqLeaseFileName(def->name)))
|
||||
goto cleanup;
|
||||
|
||||
if (!(customleasefile = networkDnsmasqLeaseFileNameCustom(def->bridge)))
|
||||
goto cleanup;
|
||||
|
||||
if (!(radvdconfigfile = networkRadvdConfigFileName(def->name)))
|
||||
goto cleanup;
|
||||
|
||||
@ -280,6 +294,7 @@ networkRemoveInactive(virNetworkDriverStatePtr driver,
|
||||
/* dnsmasq */
|
||||
dnsmasqDelete(dctx);
|
||||
unlink(leasefile);
|
||||
unlink(customleasefile);
|
||||
unlink(configfile);
|
||||
|
||||
/* radvd */
|
||||
@ -297,6 +312,7 @@ networkRemoveInactive(virNetworkDriverStatePtr driver,
|
||||
cleanup:
|
||||
VIR_FREE(leasefile);
|
||||
VIR_FREE(configfile);
|
||||
VIR_FREE(customleasefile);
|
||||
VIR_FREE(radvdconfigfile);
|
||||
VIR_FREE(radvdpidbase);
|
||||
VIR_FREE(statusfile);
|
||||
@ -1236,6 +1252,7 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network,
|
||||
int ret = -1;
|
||||
char *configfile = NULL;
|
||||
char *configstr = NULL;
|
||||
char *leaseshelper_path;
|
||||
|
||||
network->dnsmasqPid = -1;
|
||||
|
||||
@ -1258,11 +1275,21 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network,
|
||||
|
||||
cmd = virCommandNew(dnsmasqCapsGetBinaryPath(caps));
|
||||
virCommandAddArgFormat(cmd, "--conf-file=%s", configfile);
|
||||
|
||||
/* This helper is used to create custom leases file for libvirt */
|
||||
if (!(leaseshelper_path = virFileFindResource("libvirt_leaseshelper",
|
||||
"src",
|
||||
LIBEXECDIR)))
|
||||
goto cleanup;
|
||||
|
||||
virCommandAddArgFormat(cmd, "--dhcp-script=%s", leaseshelper_path);
|
||||
|
||||
*cmdout = cmd;
|
||||
ret = 0;
|
||||
cleanup:
|
||||
VIR_FREE(configfile);
|
||||
VIR_FREE(configstr);
|
||||
VIR_FREE(leaseshelper_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
360
src/network/leaseshelper.c
Normal file
360
src/network/leaseshelper.c
Normal file
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* leaseshelper.c: Helper program to create custom leases file
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
* Copyright (C) 2014 Nehal J Wani
|
||||
*
|
||||
* 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: Nehal J Wani <nehaljw.kkd1@gmail.com>
|
||||
*
|
||||
* For IPv6 support, use dnsmasq >= 2.67
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "virutil.h"
|
||||
#include "virthread.h"
|
||||
#include "virfile.h"
|
||||
#include "virpidfile.h"
|
||||
#include "virbuffer.h"
|
||||
#include "virstring.h"
|
||||
#include "virerror.h"
|
||||
#include "viralloc.h"
|
||||
#include "virjson.h"
|
||||
#include "configmake.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_NETWORK
|
||||
|
||||
/**
|
||||
* VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX:
|
||||
*
|
||||
* Macro providing the upper limit on the size of leases file
|
||||
*/
|
||||
#define VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX (32 * 1024 * 1024)
|
||||
|
||||
static const char *program_name;
|
||||
|
||||
/* Display version information. */
|
||||
static void
|
||||
helperVersion(const char *argv0)
|
||||
{
|
||||
printf("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
|
||||
}
|
||||
|
||||
ATTRIBUTE_NORETURN static void
|
||||
usage(int status)
|
||||
{
|
||||
if (status) {
|
||||
fprintf(stderr, _("%s: try --help for more details\n"), program_name);
|
||||
} else {
|
||||
printf(_("Usage: %s add|old|del mac|clientid ip [hostname]\n"
|
||||
"Designed for use with 'dnsmasq --dhcp-script'\n"
|
||||
"Refer to man page of dnsmasq for more details'\n"),
|
||||
program_name);
|
||||
}
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static int
|
||||
customLeaseRewriteFile(int fd, void *opaque)
|
||||
{
|
||||
char **data = opaque;
|
||||
|
||||
if (safewrite(fd, *data, strlen(*data)) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flags denoting actions for a lease */
|
||||
enum virLeaseActionFlags {
|
||||
VIR_LEASE_ACTION_ADD, /* Create new lease */
|
||||
VIR_LEASE_ACTION_OLD, /* Lease already exists, renew it */
|
||||
VIR_LEASE_ACTION_DEL, /* Delete the lease */
|
||||
|
||||
VIR_LEASE_ACTION_LAST
|
||||
};
|
||||
|
||||
VIR_ENUM_DECL(virLeaseAction);
|
||||
|
||||
VIR_ENUM_IMPL(virLeaseAction, VIR_LEASE_ACTION_LAST,
|
||||
"add", "old", "del");
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *exptime = NULL;
|
||||
char *pid_file = NULL;
|
||||
char *lease_entries = NULL;
|
||||
char *custom_lease_file = NULL;
|
||||
const char *ip = NULL;
|
||||
const char *mac = NULL;
|
||||
const char *iaid = virGetEnvAllowSUID("DNSMASQ_IAID");
|
||||
const char *clientid = virGetEnvAllowSUID("DNSMASQ_CLIENT_ID");
|
||||
const char *interface = virGetEnvAllowSUID("DNSMASQ_INTERFACE");
|
||||
const char *exptime_tmp = virGetEnvAllowSUID("DNSMASQ_LEASE_EXPIRES");
|
||||
const char *hostname = virGetEnvAllowSUID("DNSMASQ_SUPPLIED_HOSTNAME");
|
||||
const char *leases_str = NULL;
|
||||
long long currtime = 0;
|
||||
long long expirytime = 0;
|
||||
size_t i = 0;
|
||||
int size = 0;
|
||||
int action = -1;
|
||||
int pid_file_fd = -1;
|
||||
int rv = EXIT_FAILURE;
|
||||
int custom_lease_file_len = 0;
|
||||
bool add = false;
|
||||
bool delete = false;
|
||||
virJSONValuePtr lease_new = NULL;
|
||||
virJSONValuePtr lease_tmp = NULL;
|
||||
virJSONValuePtr leases_array = NULL;
|
||||
virJSONValuePtr leases_array_new = NULL;
|
||||
|
||||
virSetErrorFunc(NULL, NULL);
|
||||
virSetErrorLogPriorityFunc(NULL);
|
||||
|
||||
program_name = argv[0];
|
||||
|
||||
if (setlocale(LC_ALL, "") == NULL ||
|
||||
bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
|
||||
textdomain(PACKAGE) == NULL) {
|
||||
fprintf(stderr, _("%s: initialization failed\n"), program_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (virThreadInitialize() < 0 ||
|
||||
virErrorInitialize() < 0) {
|
||||
fprintf(stderr, _("%s: initialization failed\n"), program_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Doesn't hurt to check */
|
||||
if (argc > 1) {
|
||||
if (STREQ(argv[1], "--help"))
|
||||
usage(EXIT_SUCCESS);
|
||||
|
||||
if (STREQ(argv[1], "--version")) {
|
||||
helperVersion(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc != 4 && argc != 5) {
|
||||
/* Refer man page of dnsmasq --dhcp-script for more details */
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Make sure dnsmasq knows the interface. The interface name is not known
|
||||
* when dnsmasq (re)starts and throws 'del' events for expired leases.
|
||||
* So, if any old lease has expired, it will be automatically removed the
|
||||
* next time this program is invoked */
|
||||
if (!interface)
|
||||
goto cleanup;
|
||||
|
||||
ip = argv[3];
|
||||
mac = argv[2];
|
||||
action = virLeaseActionTypeFromString(argv[1]);
|
||||
|
||||
/* In case hostname is known, it is the 5th argument */
|
||||
if (argc == 5)
|
||||
hostname = argv[4];
|
||||
|
||||
if (VIR_STRDUP(exptime, exptime_tmp) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Removed extraneous trailing space in DNSMASQ_LEASE_EXPIRES (dnsmasq < 2.52) */
|
||||
if (exptime[strlen(exptime) - 1] == ' ')
|
||||
exptime[strlen(exptime) - 1] = '\0';
|
||||
|
||||
/* Check if it is an IPv6 lease */
|
||||
if (virGetEnvAllowSUID("DNSMASQ_IAID")) {
|
||||
mac = virGetEnvAllowSUID("DNSMASQ_MAC");
|
||||
clientid = argv[2];
|
||||
}
|
||||
|
||||
if (virAsprintf(&custom_lease_file,
|
||||
LOCALSTATEDIR "/lib/libvirt/dnsmasq/%s.status",
|
||||
interface) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (VIR_STRDUP(pid_file, LOCALSTATEDIR "/run/leaseshelper.pid") < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Try to claim the pidfile, exiting if we can't */
|
||||
if ((pid_file_fd = virPidFileAcquirePath(pid_file, true, getpid())) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Since interfaces can be hot plugged, we need to make sure that the
|
||||
* corresponding custom lease file exists. If not, 'touch' it */
|
||||
if (virFileTouch(custom_lease_file, 0644) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Read entire contents */
|
||||
if ((custom_lease_file_len = virFileReadAll(custom_lease_file,
|
||||
VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX,
|
||||
&lease_entries)) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (action == VIR_LEASE_ACTION_ADD ||
|
||||
action == VIR_LEASE_ACTION_OLD ||
|
||||
action == VIR_LEASE_ACTION_DEL) {
|
||||
/* Custom ipv6 leases *will not* be created if the env-var DNSMASQ_MAC
|
||||
* is not set. In the special case, when the $(interface).status file
|
||||
* is not already present and dnsmasq is (re)started, the corresponding
|
||||
* ipv6 custom lease will be created only when the guest sends the
|
||||
* 'old' action for its existing ipv6 interfaces.
|
||||
*
|
||||
* According to rfc3315, the combination of DUID and IAID can be used
|
||||
* to uniquely identify each ipv6 guest interface. So, in future, if
|
||||
* we introduce virNetworkGetDHCPLeaseBy(IAID|DUID|IAID+DUID) for ipv6
|
||||
* interfaces, then, the following if condition won't be required, as
|
||||
* the new lease will be created irrespective of whether the MACID is
|
||||
* known or not.
|
||||
*/
|
||||
if (mac || action == VIR_LEASE_ACTION_DEL) {
|
||||
/* Delete the corresponding lease, if it already exists */
|
||||
delete = true;
|
||||
if (action == VIR_LEASE_ACTION_ADD ||
|
||||
action == VIR_LEASE_ACTION_OLD) {
|
||||
add = true;
|
||||
/* Create new lease */
|
||||
if (!(lease_new = virJSONValueNewObject())) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to create json"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virStrToLong_ll(exptime, NULL, 10, &expirytime) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unable to convert lease expiry time to long long: %s"),
|
||||
exptime);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (iaid && virJSONValueObjectAppendString(lease_new, "iaid", iaid) < 0)
|
||||
goto cleanup;
|
||||
if (ip && virJSONValueObjectAppendString(lease_new, "ip-address", ip) < 0)
|
||||
goto cleanup;
|
||||
if (mac && virJSONValueObjectAppendString(lease_new, "mac-address", mac) < 0)
|
||||
goto cleanup;
|
||||
if (hostname && virJSONValueObjectAppendString(lease_new, "hostname", hostname) < 0)
|
||||
goto cleanup;
|
||||
if (clientid && virJSONValueObjectAppendString(lease_new, "client-id", clientid) < 0)
|
||||
goto cleanup;
|
||||
if (expirytime && virJSONValueObjectAppendNumberLong(lease_new, "expiry-time", expirytime) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, _("Unsupported action: %s\n"),
|
||||
virLeaseActionTypeToString(action));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* Check for previous leases */
|
||||
if (custom_lease_file_len) {
|
||||
if (!(leases_array = virJSONValueFromString(lease_entries))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("invalid json in file: %s"), custom_lease_file);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((size = virJSONValueArraySize(leases_array)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("couldn't fetch array of leases"));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(leases_array_new = virJSONValueNewArray())) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to create json"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
currtime = (long long) time(NULL);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
const char *ip_tmp = NULL;
|
||||
long long expirytime_tmp = -1;
|
||||
|
||||
if (!(lease_tmp = virJSONValueArrayGet(leases_array, i))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to parse json"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address")) ||
|
||||
(virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", &expirytime_tmp) < 0)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to parse json"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Check whether lease has expired or not */
|
||||
if (expirytime_tmp < currtime)
|
||||
continue;
|
||||
|
||||
/* Check whether lease has to be included or not */
|
||||
if (delete && STREQ(ip_tmp, ip))
|
||||
continue;
|
||||
|
||||
/* Add old lease to new array */
|
||||
if (virJSONValueArrayAppend(leases_array_new, lease_tmp) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to create json"));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (add) {
|
||||
if (virJSONValueArrayAppend(leases_array_new, lease_new) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to create json"));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(leases_str = virJSONValueToString(leases_array_new, true))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("empty json array"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Write to file */
|
||||
if (virFileRewrite(custom_lease_file, 0644,
|
||||
customLeaseRewriteFile, &leases_str) < 0)
|
||||
goto cleanup;
|
||||
|
||||
rv = EXIT_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
if (pid_file_fd != -1)
|
||||
virPidFileReleasePath(pid_file, pid_file_fd);
|
||||
|
||||
VIR_FREE(pid_file);
|
||||
VIR_FREE(exptime_tmp);
|
||||
VIR_FREE(custom_lease_file);
|
||||
virJSONValueFree(lease_new);
|
||||
virJSONValueFree(leases_array);
|
||||
virJSONValueFree(leases_array_new);
|
||||
|
||||
return rv;
|
||||
}
|
Loading…
Reference in New Issue
Block a user