[libvirt] [PATCH v4 0/3] bhyve: implement PCI address allocation
by Roman Bogorodskiy
Changes from v3:
- Instead of adding pci allocation code to utils/virpci.[ch],
create new files conf/domain_addr.[ch] to contain only
domain device addresses management
Changes from v2:
- Base on PCI allocation code from Qemu driver
Changes from v1:
- Reserve slot 1 for LPC PCI-ISA bridge, used by console device
- Respect addresses provided by user in domain xml file
- Fix tests so 'make check' passes
Roman Bogorodskiy (3):
qemu: extract PCI handling structs
qemu: extract common PCI handling functions
bhyve: implement PCI address allocation
po/POTFILES.in | 2 +
src/Makefile.am | 5 +
src/bhyve/bhyve_command.c | 142 ++--
src/bhyve/bhyve_device.c | 174 +++++
src/bhyve/bhyve_device.h | 38 ++
src/bhyve/bhyve_domain.c | 75 ++
src/bhyve/bhyve_domain.h | 39 ++
src/bhyve/bhyve_driver.c | 12 +-
src/conf/domain_addr.c | 566 ++++++++++++++++
src/conf/domain_addr.h | 149 ++++
src/libvirt_private.syms | 17 +
src/qemu/qemu_command.c | 754 +++------------------
src/qemu/qemu_command.h | 49 +-
src/qemu/qemu_domain.c | 3 +-
src/qemu/qemu_domain.h | 6 +-
src/qemu/qemu_hotplug.c | 8 +-
src/qemu/qemu_process.c | 2 +-
.../bhyvexml2argvdata/bhyvexml2argv-acpiapic.args | 2 +-
tests/bhyvexml2argvdata/bhyvexml2argv-acpiapic.xml | 2 +
tests/bhyvexml2argvdata/bhyvexml2argv-base.args | 2 +-
tests/bhyvexml2argvdata/bhyvexml2argv-base.xml | 2 +
tests/bhyvexml2argvdata/bhyvexml2argv-console.args | 4 +-
tests/bhyvexml2argvdata/bhyvexml2argv-console.xml | 2 +
.../bhyvexml2argv-disk-virtio.args | 2 +-
.../bhyvexml2argv-disk-virtio.xml | 2 +
tests/bhyvexml2argvdata/bhyvexml2argv-macaddr.args | 2 +-
tests/bhyvexml2argvdata/bhyvexml2argv-macaddr.xml | 2 +
tests/bhyvexml2argvdata/bhyvexml2argv-serial.args | 4 +-
tests/bhyvexml2argvdata/bhyvexml2argv-serial.xml | 2 +
29 files changed, 1287 insertions(+), 782 deletions(-)
create mode 100644 src/bhyve/bhyve_device.c
create mode 100644 src/bhyve/bhyve_device.h
create mode 100644 src/bhyve/bhyve_domain.c
create mode 100644 src/bhyve/bhyve_domain.h
create mode 100644 src/conf/domain_addr.c
create mode 100644 src/conf/domain_addr.h
--
1.9.0
10 years, 7 months
[libvirt] [PATCH V3 0/7] Honor DAC norelabel attribute
by Jim Fehlig
V3 of Michal's series to honor relabel='no' in device config
https://www.redhat.com/archives/libvir-list/2014-April/msg00196.html
In V3, the patches have been further split to ease review as requested
by Jan Tomko.
Jim Fehlig (7):
security_dac: annotate some functions with ATTRIBUTE_NONNULL
security_dac: cleanup use of enum types
security_dac: rework callback parameter passing
security_dac: avoid relabeling when relabel='no'
security_dac: honor relabel='no' in disk config
security_dac: avoid relabeling hostdevs when relabel='no'
security_dac: honor relabel='no' in chardev config
src/security/security_dac.c | 333 +++++++++++++++++++++++++++-----------------
1 file changed, 203 insertions(+), 130 deletions(-)
--
1.8.1.4
10 years, 7 months
[libvirt] [PATCH 0/4] more enum cleanups
by Eric Blake
Inspired by the cleanups contributed by Julio Faracco, I did some
more cleanups of my own. My end goal is to turn on a syntax-check
rule that forbids 'enum vir' (since we should always be declaring
'typedef enum { ... } vir...;'), but there's still a lot of
violations in the code base, even after this series.
Eric Blake (4):
vbox: fix stale comment about vdi storage type
maint: use enum typedef for virstorageencryption.h
maint: shorten 'TypeType' function names
maint: prefer enum over int for virstoragefile structs
src/conf/domain_conf.c | 14 +++++++-------
src/conf/domain_conf.h | 2 +-
src/conf/secret_conf.c | 8 ++++----
src/conf/secret_conf.h | 4 ++--
src/conf/storage_conf.c | 14 +++++++-------
src/conf/storage_conf.h | 6 +++---
src/libvirt_private.syms | 10 +++++-----
src/lxc/lxc_controller.c | 2 +-
src/qemu/qemu_command.c | 8 ++++++++
src/qemu/qemu_driver.c | 8 ++++----
src/storage/storage_backend_disk.c | 2 +-
src/util/virstorageencryption.c | 6 +++---
src/util/virstorageencryption.h | 10 +++++-----
src/util/virstoragefile.h | 19 ++++++++++---------
src/vbox/vbox_tmpl.c | 15 ++++-----------
tools/virsh-secret.c | 4 ++--
16 files changed, 67 insertions(+), 65 deletions(-)
--
1.9.0
10 years, 7 months
[libvirt] [PATCH] maint: fix typos related to disk name resolution
by Eric Blake
In a number of APIs, the text implied that a user might have
<target dev='xvda'/> - but common convention is to use "vda",
not "xvda". For example, virDomainGetDiskErrors was correct,
while virDomainBlockStats was confusing.
* src/libvirt.c: Make examples consistent.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
Copy and paste is to blame for the size of this patch :)
Pushing under the trivial rule.
src/libvirt.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c
index ccb7113..19fa18b 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -7850,7 +7850,7 @@ virDomainSetSchedulerParametersFlags(virDomainPtr domain,
* devices attached to the domain.
*
* The @disk parameter is either the device target shorthand (the
- * <target dev='...'/> sub-element, such as "xvda"), or (since 0.9.8)
+ * <target dev='...'/> sub-element, such as "vda"), or (since 0.9.8)
* an unambiguous source name of the block device (the <source
* file='...'/> sub-element, such as "/path/to/image"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
@@ -7918,7 +7918,7 @@ virDomainBlockStats(virDomainPtr dom, const char *disk,
* devices attached to the domain.
*
* The @disk parameter is either the device target shorthand (the
- * <target dev='...'/> sub-element, such as "xvda"), or (since 0.9.8)
+ * <target dev='...'/> sub-element, such as "vda"), or (since 0.9.8)
* an unambiguous source name of the block device (the <source
* file='...'/> sub-element, such as "/path/to/image"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
@@ -8270,7 +8270,7 @@ virDomainMemoryStats(virDomainPtr dom, virDomainMemoryStatPtr stats,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or (since 0.9.5) the device target shorthand
- * (the <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * (the <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -8348,7 +8348,7 @@ virDomainBlockPeek(virDomainPtr dom,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or (since 0.9.5) the device target shorthand
- * (the <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * (the <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -8510,7 +8510,7 @@ virDomainMemoryPeek(virDomainPtr dom,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or (since 0.9.5) the device target shorthand
- * (the <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * (the <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -19448,7 +19448,7 @@ virDomainOpenChannel(virDomainPtr dom,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or (since 0.9.5) the device target shorthand
- * (the <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * (the <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -19522,7 +19522,7 @@ virDomainBlockJobAbort(virDomainPtr dom, const char *disk,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or (since 0.9.5) the device target shorthand
- * (the <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * (the <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -19576,7 +19576,7 @@ virDomainGetBlockJobInfo(virDomainPtr dom, const char *disk,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or (since 0.9.5) the device target shorthand
- * (the <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * (the <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -19633,7 +19633,7 @@ virDomainBlockJobSetSpeed(virDomainPtr dom, const char *disk,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or (since 0.9.5) the device target shorthand
- * (the <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * (the <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -19744,7 +19744,7 @@ virDomainBlockPull(virDomainPtr dom, const char *disk,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or the device target shorthand (the
- * <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
@@ -19867,7 +19867,7 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk,
* The @disk parameter is either an unambiguous source name of the
* block device (the <source file='...'/> sub-element, such as
* "/path/to/image"), or the device target shorthand (the
- * <target dev='...'/> sub-element, such as "xvda"). Valid names
+ * <target dev='...'/> sub-element, such as "vda"). Valid names
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
--
1.9.0
10 years, 7 months
[libvirt] [PATCH 0/2] Fix seclabels for chardevs
by Ján Tomko
Ján Tomko (2):
Rename virDomainDiskSourceDefFormatSeclabel
Fix seclabels for chardevs
src/conf/domain_conf.c | 51 ++++++++++------------
.../qemuxml2argv-chardev-label.xml | 40 +++++++++++++++++
tests/qemuxml2xmltest.c | 2 +
3 files changed, 66 insertions(+), 27 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-chardev-label.xml
--
1.8.3.2
10 years, 7 months
[libvirt] Quorum block driver libvirt support proposal
by Benoît Canet
Hello list,
I want to implement libvirt Quorum support.
(https://github.com/qemu/qemu/commit/c88a1de51ab2f26a9a37ffc317249736de8c015c)
Quorum is a QEMU RAID like block storage driver.
Data are written on n replicas and when a read is done a comparison between the
replica read is done. If more than threshold reads are identical the read succeed
else it's and error.
For example a Quorum with n = 3 and threshold = 2 would be made of three QCOW2
backing chains used as identicals replicas. threshold = 2 means that at least
2 replica must be identical when doing a read.
I want to make use of the new backingStore xml element to implement quorum.
Proposed Quorum libvirt format:
-------------------------------
<disk type='quorum' device='disk'>
<driver name='qemu' type='quorum'/>
<threshold value=2/>
<backingStore type='file'>
<format type='qcow2'/>
<source file='/var/lib/libvirt/images/file1.qcow2'/>
</backingStore>
<backingStore type='file'>
<format type='qcow2'/>
<source file='/var/lib/libvirt/images/file2.qcow2'/>
</backingStore>
<backingStore type='file'>
<format type='qcow2'/>
<source file='/var/lib/libvirt/images/file3.qcow2'/>
</backingStore>
<target dev='vda' bus='virtio'/>
</disk>
Implementation plan:
--------------------
* Add VIR_STORAGE_TYPE_QUORUM
* In src/util/virstoragefile.h change _virStorageSource to contain a
virStorageSourcePtrPtr backingStores.
I think doing it at this level allow to keep a 1-1 mapping with the qemu
BlockDriverState hiearchy
* Add a int quorum_threshold field to the same structure
* Add support for parsing treshold in virDomainDiskDefParseXML
* Change virDomainDiskBackingStoreParse to virDomainDiskBackingStoresParse to parse
all the backingStore at once an use realloc to grow the backingStores field.
* Modify virDomainDiskDefFormat to call virDomainDiskBackingStoreFormat in a loop
for saving
* hook into qemuBuildDriveStr around line 3442 to create the quorum parameters
Do you feel that I am missing something ?
Best regards
Benoît
10 years, 7 months
[libvirt] [RFC 0/5] Allow object-add on X86CPU subclasses, for CPU model probing
by Eduardo Habkost
This series allows management code to use object-add on X86CPU subclasses, so it
can use it to probe for CPU model information without re-running QEMU. The main
use case for this is to allow management code to create CPU objects and query
the "feature-words" and "filtered-features" properties on the new objects, to
find out which features each CPU model needs, and to do the same using the
"host" CPU model to check which features can be enabled in a given host.
There's experimental libvirt code to use the new command at:
https://github.com/ehabkost/libvirt/tree/work/cpu-feature-word-query
The experimental code just create the CPU objects to query for feature
information, but doesn't do anything with that data.
Eduardo Habkost (5):
cpu: Initialize cpu->stopped=true earlier
cpu: Don't try to pause CPUs if they are already stopped
pc: Don't crash on apic_accept_pic_intr() if CPU has no apic_state
target-i386: Make CPU objects user-creatable
target-i386: Report QOM class name for CPU definitions
cpus.c | 13 ++++++++++---
exec.c | 1 +
hw/i386/pc.c | 2 +-
qapi-schema.json | 6 +++++-
target-i386/cpu.c | 7 +++++++
5 files changed, 24 insertions(+), 5 deletions(-)
--
1.9.0
10 years, 7 months
[libvirt] [PATCH] vircgroup: Don't leak keypath if failed to kill process
by Chen Hanxiao
Signed-off-by: Chen Hanxiao <chenhanxiao(a)cn.fujitsu.com>
---
src/util/vircgroup.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c
index fce380a..c578bd0 100644
--- a/src/util/vircgroup.c
+++ b/src/util/vircgroup.c
@@ -3370,7 +3370,7 @@ virCgroupKillRecursiveInternal(virCgroupPtr group,
int rc;
bool killedAny = false;
char *keypath = NULL;
- DIR *dp;
+ DIR *dp = NULL;
virCgroupPtr subgroup = NULL;
struct dirent *ent;
int direrr;
@@ -3381,7 +3381,7 @@ virCgroupKillRecursiveInternal(virCgroupPtr group,
return -1;
if ((rc = virCgroupKillInternal(group, signum, pids)) < 0)
- return -1;
+ goto cleanup;
if (rc == 1)
killedAny = true;
@@ -3394,7 +3394,7 @@ virCgroupKillRecursiveInternal(virCgroupPtr group,
}
virReportSystemError(errno,
_("Cannot open %s"), keypath);
- return -1;
+ goto cleanup;
}
while ((direrr = virDirRead(dp, &ent, keypath)) > 0) {
@@ -3429,7 +3429,9 @@ virCgroupKillRecursiveInternal(virCgroupPtr group,
cleanup:
virCgroupFree(&subgroup);
- closedir(dp);
+ VIR_FREE(keypath);
+ if (dp)
+ closedir(dp);
return ret;
}
--
1.9.0
10 years, 7 months
[libvirt] [libvirt-python PATCH v2] override: add virDomainFSFreeze and virDomainFSThaw API
by Michal Privoznik
From: Tomoki Sekiyama <tomoki.sekiyama(a)hds.com>
Add binding for the new virDomainFSFreeze and virDomainFSThaw functions
added in libvirt 1.2.5. These require override since these take a list
of mountpoints path string. The methods are named 'fsFreeze' and
'fsThaw'.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama(a)hds.com>
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
diff to v1:
-Eric's and mine review comments worked in
generator.py | 3 ++
libvirt-override-virDomain.py | 12 ++++++
libvirt-override.c | 97 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 112 insertions(+)
diff --git a/generator.py b/generator.py
index 05ec743..e7b4643 100755
--- a/generator.py
+++ b/generator.py
@@ -517,6 +517,9 @@ skip_function = (
'virDomainCreateXMLWithFiles', # overridden in virConnect.py
'virDomainCreateWithFiles', # overridden in virDomain.py
+ 'virDomainFSFreeze', # overridden in virDomain.py
+ 'virDomainFSThaw', # overridden in virDomain.py
+
# 'Ref' functions have no use for bindings users.
"virConnectRef",
"virDomainRef",
diff --git a/libvirt-override-virDomain.py b/libvirt-override-virDomain.py
index c96cc5e..e61ad00 100644
--- a/libvirt-override-virDomain.py
+++ b/libvirt-override-virDomain.py
@@ -47,3 +47,15 @@
ret = libvirtmod.virDomainCreateWithFiles(self._o, files, flags)
if ret == -1: raise libvirtError ('virDomainCreateWithFiles() failed', dom=self)
return ret
+
+ def fsFreeze(self, mountpoints=None, flags=0):
+ """Freeze specified filesystems within the guest """
+ ret = libvirtmod.virDomainFSFreeze(self._o, mountpoints, flags)
+ if ret == -1: raise libvirtError ('virDomainFSFreeze() failed', dom=self)
+ return ret
+
+ def fsThaw(self, mountpoints=None, flags=0):
+ """Thaw specified filesystems within the guest """
+ ret = libvirtmod.virDomainFSThaw(self._o, mountpoints, flags)
+ if ret == -1: raise libvirtError ('virDomainFSThaw() failed', dom=self)
+ return ret
diff --git a/libvirt-override.c b/libvirt-override.c
index 3fa9b9b..c4ac223 100644
--- a/libvirt-override.c
+++ b/libvirt-override.c
@@ -7554,6 +7554,99 @@ cleanup:
#endif /* LIBVIR_CHECK_VERSION(1, 1, 1) */
+#if LIBVIR_CHECK_VERSION(1, 2, 5)
+static PyObject *
+libvirt_virDomainFSFreeze(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
+ PyObject *py_retval = NULL;
+ int c_retval;
+ virDomainPtr domain;
+ PyObject *pyobj_domain;
+ PyObject *pyobj_list;
+ unsigned int flags;
+ unsigned int nmountpoints = 0;
+ char **mountpoints = NULL;
+ size_t i = 0, j;
+
+ if (!PyArg_ParseTuple(args, (char *)"OOi:virDomainFSFreeze",
+ &pyobj_domain, &pyobj_list, &flags))
+ return NULL;
+ domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain);
+
+ if (PyList_Check(pyobj_list)) {
+ nmountpoints = PyList_Size(pyobj_list);
+
+ if (VIR_ALLOC_N(mountpoints, nmountpoints) < 0)
+ return PyErr_NoMemory();
+
+ for (i = 0; i < nmountpoints; i++) {
+ if (libvirt_charPtrUnwrap(PyList_GetItem(pyobj_list, i),
+ mountpoints+i) < 0 ||
+ mountpoints[i] == NULL)
+ goto cleanup;
+ }
+ }
+
+ LIBVIRT_BEGIN_ALLOW_THREADS;
+ c_retval = virDomainFSFreeze(domain, (const char **) mountpoints,
+ nmountpoints, flags);
+ LIBVIRT_END_ALLOW_THREADS;
+
+ py_retval = libvirt_intWrap(c_retval);
+
+cleanup:
+ for (j = 0 ; j < i ; j++)
+ VIR_FREE(mountpoints[j]);
+ VIR_FREE(mountpoints);
+ return py_retval;
+}
+
+
+static PyObject *
+libvirt_virDomainFSThaw(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
+ PyObject *py_retval = NULL;
+ int c_retval;
+ virDomainPtr domain;
+ PyObject *pyobj_domain;
+ PyObject *pyobj_list;
+ unsigned int flags;
+ unsigned int nmountpoints = 0;
+ char **mountpoints = NULL;
+ size_t i = 0, j;
+
+ if (!PyArg_ParseTuple(args, (char *)"OOi:virDomainFSThaw",
+ &pyobj_domain, &pyobj_list, &flags))
+ return NULL;
+ domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain);
+
+ if (PyList_Check(pyobj_list)) {
+ nmountpoints = PyList_Size(pyobj_list);
+
+ if (VIR_ALLOC_N(mountpoints, nmountpoints) < 0)
+ return PyErr_NoMemory();
+
+ for (i = 0; i < nmountpoints; i++) {
+ if (libvirt_charPtrUnwrap(PyList_GetItem(pyobj_list, i),
+ mountpoints+i) < 0 ||
+ mountpoints[i] == NULL)
+ goto cleanup;
+ }
+ }
+
+ LIBVIRT_BEGIN_ALLOW_THREADS;
+ c_retval = virDomainFSThaw(domain, (const char **) mountpoints,
+ nmountpoints, flags);
+ LIBVIRT_END_ALLOW_THREADS;
+
+ py_retval = libvirt_intWrap(c_retval);
+
+ cleanup:
+ for (j = 0 ; j < i ; j++)
+ VIR_FREE(mountpoints[j]);
+ VIR_FREE(mountpoints);
+ return py_retval;
+}
+#endif /* LIBVIR_CHECK_VERSION(1, 2, 5) */
+
/************************************************************************
* *
* The registration stuff *
@@ -7729,6 +7822,10 @@ static PyMethodDef libvirtMethods[] = {
{(char *) "virDomainCreateXMLWithFiles", libvirt_virDomainCreateXMLWithFiles, METH_VARARGS, NULL},
{(char *) "virDomainCreateWithFiles", libvirt_virDomainCreateWithFiles, METH_VARARGS, NULL},
#endif /* LIBVIR_CHECK_VERSION(1, 1, 1) */
+#if LIBVIR_CHECK_VERSION(1, 2, 5)
+ {(char *) "virDomainFSFreeze", libvirt_virDomainFSFreeze, METH_VARARGS, NULL},
+ {(char *) "virDomainFSThaw", libvirt_virDomainFSThaw, METH_VARARGS, NULL},
+#endif /* LIBVIR_CHECK_VERSION(1, 2, 5) */
{NULL, NULL, 0, NULL}
};
--
1.9.3
10 years, 7 months