[libvirt] [PATCHv2 0/5] manage the shmem device source

v1 link: https://www.redhat.com/archives/libvir-list/2015-July/msg00926.html v2 different: -Introduce new xml element shareable to indicate whether the device is intended to be shared across multiple guests. -Make virSecuritySELinuxRestoreSecurityAllLabel and virSecuritySELinuxSetSecurityAllLabel call set/restore label -Move struct _virShmObject and _virShmObjectList in virshm.c and introduce some functions to get the parameter. -Change the return value to -1 when the function are not supported on some platform. -add the code in src/security/security_nop.c -some small pieces fix Since there is a shmobj leak when let qemu create shmobj by themselves, also the label of shmobj/shmem-server socket is not right. Guest cannot direct use the shmem-server if users enabled selinux. So it will be better to manage it in libvirt. The way i chosed is region the shmem deivce in a list, and save it status to a local file to avoid losing it after restart libvirtd, and count the guest which use it, and let the callers know if there is no guest is using it (then we can relabel/cleanup some resource). About shmem-server we decided introduce some new selinux label to fix that, however ivshmem server still not finished right now (i mean patches were still being reviewed in qemu-devel), we could implement the set up ivshmem-server part later. BTW, during some research i noticed the vhost-user network should have the same problem like the ivshmem-server, since it use the same way to connect to guest (a host app connect to guest via socket), and after some test i found the guest cannot connect to the sockect which created by app if i enable the Selinux. So i think maybe we could fix these two issue together, if there is already have a way to make vhost-usr works well with SElinux, i think we could use that way in this place. Luyao Huang (5): conf: introduce seclabels in shmem device element conf: introduce shareable in shmem device element util: introduce new helpers to manage shmem device security: add security part for shmem device qemu: call the helpers in virshm.c to manage shmem device configure.ac | 10 + docs/formatdomain.html.in | 12 + docs/schemas/domaincommon.rng | 8 + po/POTFILES.in | 3 +- src/Makefile.am | 7 +- src/conf/domain_conf.c | 103 ++- src/conf/domain_conf.h | 6 + src/libvirt_private.syms | 26 + src/qemu/qemu_conf.h | 3 + src/qemu/qemu_driver.c | 4 + src/qemu/qemu_process.c | 157 +++++ src/security/security_dac.c | 100 +++ src/security/security_driver.h | 9 + src/security/security_manager.c | 35 + src/security/security_manager.h | 6 + src/security/security_nop.c | 19 + src/security/security_selinux.c | 97 +++ src/security/security_stack.c | 39 ++ src/util/virshm.c | 739 +++++++++++++++++++++ src/util/virshm.h | 92 +++ .../qemuxml2argv-shmem-seclabel.xml | 55 ++ .../qemuxml2argv-shmem-shareable.xml | 43 ++ tests/qemuxml2xmltest.c | 5 + 23 files changed, 1545 insertions(+), 33 deletions(-) create mode 100644 src/util/virshm.c create mode 100644 src/util/virshm.h create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-shmem-seclabel.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-shmem-shareable.xml -- 1.8.3.1

Introduce a new element in shmem device element, this could help users to change the shm label to a specified label. Signed-off-by: Luyao Huang <lhuang@redhat.com> --- docs/formatdomain.html.in | 7 ++ docs/schemas/domaincommon.rng | 3 + src/conf/domain_conf.c | 97 +++++++++++++++------- src/conf/domain_conf.h | 5 ++ .../qemuxml2argv-shmem-seclabel.xml | 55 ++++++++++++ tests/qemuxml2xmltest.c | 4 + 6 files changed, 141 insertions(+), 30 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-shmem-seclabel.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 5ca8ede..f2ac5fb 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -6195,6 +6195,13 @@ qemu-kvm -net nic,model=? /dev/null vectors. The <code>ioeventd</code> attribute enables/disables (values "on"/"off", respectively) ioeventfd. </dd> + <dt><code>seclabel</code></dt> + <dd> + The element may contain an optional <code>seclabel</code> to override the + way that labelling is done on the shm object path or shm server path. If this + element is not present, the <a href="#seclabel">security label is inherited + from the per-domain setting</a>. + </dd> </dl> <h4><a name="elementsMemory">Memory devices</a></h4> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index ccc74cc..f13f566 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3371,6 +3371,9 @@ </optional> </element> </optional> + <zeroOrMore> + <ref name='devSeclabel'/> + </zeroOrMore> <optional> <ref name="address"/> </optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c5e9653..ece9f2d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11515,6 +11515,8 @@ virDomainNVRAMDefParseXML(xmlNodePtr node, static virDomainShmemDefPtr virDomainShmemDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, + virSecurityLabelDefPtr* vmSeclabels, + int nvmSeclabels, unsigned int flags) { char *tmp = NULL; @@ -11586,6 +11588,10 @@ virDomainShmemDefParseXML(xmlNodePtr node, if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto cleanup; + if (virSecurityDeviceLabelDefParseXML(&def->seclabels, &def->nseclabels, + vmSeclabels, nvmSeclabels, + ctxt, flags) < 0) + goto cleanup; ret = def; def = NULL; @@ -12708,7 +12714,11 @@ virDomainDeviceDefParse(const char *xmlStr, goto error; break; case VIR_DOMAIN_DEVICE_SHMEM: - if (!(dev->data.shmem = virDomainShmemDefParseXML(node, ctxt, flags))) + if (!(dev->data.shmem = virDomainShmemDefParseXML(node, + ctxt, + def->seclabels, + def->nseclabels, + flags))) goto error; break; case VIR_DOMAIN_DEVICE_TPM: @@ -16383,7 +16393,8 @@ virDomainDefParseXML(xmlDocPtr xml, for (i = 0; i < n; i++) { virDomainShmemDefPtr shmem; ctxt->node = nodes[i]; - shmem = virDomainShmemDefParseXML(nodes[i], ctxt, flags); + shmem = virDomainShmemDefParseXML(nodes[i], ctxt, def->seclabels, + def->nseclabels, flags); if (!shmem) goto error; @@ -20594,45 +20605,52 @@ virDomainShmemDefFormat(virBufferPtr buf, virDomainShmemDefPtr def, unsigned int flags) { - virBufferEscapeString(buf, "<shmem name='%s'", def->name); + virBuffer childrenBuf = VIR_BUFFER_INITIALIZER; + int indent = virBufferGetIndent(buf, false); + size_t n; - if (!def->size && - !def->server.enabled && - !def->msi.enabled && - !virDomainDeviceInfoNeedsFormat(&def->info, flags)) { - virBufferAddLit(buf, "/>\n"); - return 0; - } else { - virBufferAddLit(buf, ">\n"); - } + virBufferEscapeString(buf, "<shmem name='%s'", def->name); - virBufferAdjustIndent(buf, 2); + virBufferAdjustIndent(&childrenBuf, indent + 2); if (def->size) - virBufferAsprintf(buf, "<size unit='M'>%llu</size>\n", def->size >> 20); + virBufferAsprintf(&childrenBuf, "<size unit='M'>%llu</size>\n", + def->size >> 20); if (def->server.enabled) { - virBufferAddLit(buf, "<server"); - virBufferEscapeString(buf, " path='%s'", def->server.chr.data.nix.path); - virBufferAddLit(buf, "/>\n"); + virBufferAddLit(&childrenBuf, "<server"); + virBufferEscapeString(&childrenBuf, " path='%s'", + def->server.chr.data.nix.path); + virBufferAddLit(&childrenBuf, "/>\n"); } if (def->msi.enabled) { - virBufferAddLit(buf, "<msi"); + virBufferAddLit(&childrenBuf, "<msi"); if (def->msi.vectors) - virBufferAsprintf(buf, " vectors='%u'", def->msi.vectors); + virBufferAsprintf(&childrenBuf, " vectors='%u'", def->msi.vectors); if (def->msi.ioeventfd) - virBufferAsprintf(buf, " ioeventfd='%s'", + virBufferAsprintf(&childrenBuf, " ioeventfd='%s'", virTristateSwitchTypeToString(def->msi.ioeventfd)); - virBufferAddLit(buf, "/>\n"); + virBufferAddLit(&childrenBuf, "/>\n"); } - if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) + for (n = 0; n < def->nseclabels; n++) + virSecurityDeviceLabelDefFormat(&childrenBuf, def->seclabels[n], flags); + + if (virDomainDeviceInfoFormat(&childrenBuf, &def->info, flags) < 0) { + virBufferFreeAndReset(&childrenBuf); return -1; + } - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</shmem>\n"); + if (virBufferUse(&childrenBuf)) { + virBufferAddLit(buf, ">\n"); + virBufferAddBuffer(buf, &childrenBuf); + virBufferAddLit(buf, "</shmem>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } + virBufferFreeAndReset(&childrenBuf); return 0; } @@ -24137,6 +24155,21 @@ virDomainObjListExport(virDomainObjListPtr domlist, } +static virSecurityDeviceLabelDefPtr +virDomainGetDeviceSecurityLabelDef(virSecurityDeviceLabelDefPtr *seclabels, + size_t nseclabels, + const char *model) +{ + size_t i; + + for (i = 0; i < nseclabels; i++) { + if (STREQ_NULLABLE(seclabels[i]->model, model)) + return seclabels[i]; + } + return NULL; +} + + virSecurityLabelDefPtr virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model) { @@ -24160,16 +24193,20 @@ virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model) virSecurityDeviceLabelDefPtr virDomainChrDefGetSecurityLabelDef(virDomainChrDefPtr def, const char *model) { - size_t i; + if (def == NULL) + return NULL; + + return virDomainGetDeviceSecurityLabelDef(def->seclabels, def->nseclabels, model); +} + +virSecurityDeviceLabelDefPtr +virDomainShmemDefGetSecurityLabelDef(virDomainShmemDefPtr def, const char *model) +{ if (def == NULL) return NULL; - for (i = 0; i < def->nseclabels; i++) { - if (STREQ_NULLABLE(def->seclabels[i]->model, model)) - return def->seclabels[i]; - } - return NULL; + return virDomainGetDeviceSecurityLabelDef(def->seclabels, def->nseclabels, model); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 961e4ed..d53c36f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1642,6 +1642,8 @@ struct _virDomainShmemDef { unsigned vectors; virTristateSwitch ioeventfd; } msi; + size_t nseclabels; + virSecurityDeviceLabelDefPtr *seclabels; virDomainDeviceInfo info; }; @@ -2984,6 +2986,9 @@ virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model); virSecurityDeviceLabelDefPtr virDomainChrDefGetSecurityLabelDef(virDomainChrDefPtr def, const char *model); +virSecurityDeviceLabelDefPtr +virDomainShmemDefGetSecurityLabelDef(virDomainShmemDefPtr def, const char *model); + typedef const char* (*virEventActionToStringFunc)(int type); typedef int (*virEventActionFromStringFunc)(const char *type); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-shmem-seclabel.xml b/tests/qemuxml2argvdata/qemuxml2argv-shmem-seclabel.xml new file mode 100644 index 0000000..feb7404 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-shmem-seclabel.xml @@ -0,0 +1,55 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='none'/> + <shmem name='shmem0'> + <seclabel model='dac' relabel='no'/> + </shmem> + <shmem name='shmem1'> + <size unit='M'>128</size> + <seclabel model='dac' relabel='no'/> + </shmem> + <shmem name='shmem2'> + <size unit='M'>256</size> + <seclabel model='selinux' relabel='yes'> + <label>system_u:system_r:svirt_custom_t:s0:c192,c392</label> + </seclabel> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </shmem> + <shmem name='shmem3'> + <size unit='M'>512</size> + <server/> + <seclabel model='selinux' relabel='yes'> + <label>system_u:system_r:svirt_custom_t:s0:c192,c392</label> + </seclabel> + </shmem> + <shmem name='shmem4'> + <size unit='M'>1024</size> + <server path='/tmp/shmem4-sock'/> + <seclabel model='selinux' relabel='yes'> + <label>system_u:system_r:svirt_custom_t:s0:c192,c392</label> + </seclabel> + </shmem> + <shmem name='shmem5'> + <size unit='M'>2048</size> + <server path='/tmp/shmem5-sock'/> + <msi ioeventfd='off'/> + <seclabel model='dac' relabel='no'/> + </shmem> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 5c1c2e9..7361db5 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -620,9 +620,13 @@ mymain(void) DO_TEST("tap-vhost"); DO_TEST_DIFFERENT("tap-vhost-incorrect"); + DO_TEST("shmem"); + DO_TEST("shmem-seclabel"); + DO_TEST("smbios"); DO_TEST("smbios-multiple-type2"); + DO_TEST("aarch64-aavmf-virtio-mmio"); DO_TEST("memory-hotplug"); -- 1.8.3.1

Signed-off-by: Luyao Huang <lhuang@redhat.com> --- docs/formatdomain.html.in | 5 +++ docs/schemas/domaincommon.rng | 5 +++ src/conf/domain_conf.c | 6 +++ src/conf/domain_conf.h | 1 + .../qemuxml2argv-shmem-shareable.xml | 43 ++++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 6 files changed, 61 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-shmem-shareable.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f2ac5fb..def76fe 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -6195,6 +6195,11 @@ qemu-kvm -net nic,model=? /dev/null vectors. The <code>ioeventd</code> attribute enables/disables (values "on"/"off", respectively) ioeventfd. </dd> + <dt><code>shareable</code></dt> + <dd> + If present, this indicates the device is expected to be shared + between domains (assuming the hypervisor and OS support this). + </dd> <dt><code>seclabel</code></dt> <dd> The element may contain an optional <code>seclabel</code> to override the diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index f13f566..350f1b3 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3371,6 +3371,11 @@ </optional> </element> </optional> + <optional> + <element name="shareable"> + <empty/> + </element> + </optional> <zeroOrMore> <ref name='devSeclabel'/> </zeroOrMore> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ece9f2d..34cd93c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11585,6 +11585,9 @@ virDomainShmemDefParseXML(xmlNodePtr node, goto cleanup; } + if (virXPathBoolean("boolean(./shareable)", ctxt)) + def->shareable = true; + if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto cleanup; @@ -20634,6 +20637,9 @@ virDomainShmemDefFormat(virBufferPtr buf, virBufferAddLit(&childrenBuf, "/>\n"); } + if (def->shareable) + virBufferAddLit(&childrenBuf, "<shareable/>\n"); + for (n = 0; n < def->nseclabels; n++) virSecurityDeviceLabelDefFormat(&childrenBuf, def->seclabels[n], flags); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d53c36f..06c304d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1642,6 +1642,7 @@ struct _virDomainShmemDef { unsigned vectors; virTristateSwitch ioeventfd; } msi; + bool shareable; size_t nseclabels; virSecurityDeviceLabelDefPtr *seclabels; virDomainDeviceInfo info; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-shmem-shareable.xml b/tests/qemuxml2argvdata/qemuxml2argv-shmem-shareable.xml new file mode 100644 index 0000000..c94102c --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-shmem-shareable.xml @@ -0,0 +1,43 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='none'/> + <shmem name='shmem0'> + <shareable/> + </shmem> + <shmem name='shmem1'> + <size unit='M'>128</size> + <shareable/> + </shmem> + <shmem name='shmem2'> + <size unit='M'>256</size> + <shareable/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </shmem> + <shmem name='shmem3'> + <size unit='M'>512</size> + <server/> + <shareable/> + </shmem> + <shmem name='shmem4'> + <size unit='M'>1024</size> + <server path='/tmp/shmem4-sock'/> + <shareable/> + </shmem> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 7361db5..76df46a 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -623,6 +623,7 @@ mymain(void) DO_TEST("shmem"); DO_TEST("shmem-seclabel"); + DO_TEST("shmem-shareable"); DO_TEST("smbios"); DO_TEST("smbios-multiple-type2"); -- 1.8.3.1

Signed-off-by: Luyao Huang <lhuang@redhat.com> --- configure.ac | 10 + po/POTFILES.in | 3 +- src/Makefile.am | 7 +- src/libvirt_private.syms | 24 ++ src/util/virshm.c | 739 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virshm.h | 92 ++++++ 6 files changed, 872 insertions(+), 3 deletions(-) create mode 100644 src/util/virshm.c create mode 100644 src/util/virshm.h diff --git a/configure.ac b/configure.ac index 08a0f93..ce4908d 100644 --- a/configure.ac +++ b/configure.ac @@ -1176,6 +1176,16 @@ if test "$with_linux" = "yes"; then ]]) fi +dnl +dnl check for POSIX share memory functions +dnl +LIBRT_LIBS="" +AC_CHECK_LIB([rt],[shm_open],[LIBRT_LIBS="-lrt"]) +old_libs="$LIBS" +LIBS="$old_libs $LIBRT_LIBS" +AC_CHECK_FUNCS([shm_open]) +LIBS="$old_libs" +AC_SUBST([LIBRT_LIBS]) dnl Need to test if pkg-config exists PKG_PROG_PKG_CONFIG diff --git a/po/POTFILES.in b/po/POTFILES.in index 46220f7..189699d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -216,8 +216,9 @@ src/util/virpolkit.c src/util/virportallocator.c src/util/virprocess.c src/util/virrandom.c -src/util/virsexpr.c src/util/virscsi.c +src/util/virsexpr.c +src/util/virshm.c src/util/virsocketaddr.c src/util/virstats.c src/util/virstorageencryption.c diff --git a/src/Makefile.am b/src/Makefile.am index 57a06e8..3158b32 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -152,6 +152,7 @@ UTIL_SOURCES = \ util/virscsi.c util/virscsi.h \ util/virseclabel.c util/virseclabel.h \ util/virsexpr.c util/virsexpr.h \ + util/virshm.c util/virshm.h \ util/virsocketaddr.h util/virsocketaddr.c \ util/virstats.c util/virstats.h \ util/virstorageencryption.c util/virstorageencryption.h \ @@ -1049,7 +1050,7 @@ libvirt_util_la_LIBADD = $(CAPNG_LIBS) $(YAJL_LIBS) $(LIBNL_LIBS) \ $(THREAD_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS) \ $(LIB_CLOCK_GETTIME) $(DBUS_LIBS) $(MSCOM_LIBS) $(LIBXML_LIBS) \ $(SECDRIVER_LIBS) $(NUMACTL_LIBS) $(SYSTEMD_DAEMON_LIBS) \ - $(POLKIT_LIBS) + $(POLKIT_LIBS) $(LIBRT_LIBS) noinst_LTLIBRARIES += libvirt_conf.la @@ -1282,9 +1283,10 @@ libvirt_driver_qemu_impl_la_CFLAGS = \ $(AM_CFLAGS) libvirt_driver_qemu_impl_la_LDFLAGS = $(AM_LDFLAGS) libvirt_driver_qemu_impl_la_LIBADD = $(CAPNG_LIBS) \ - $(GNUTLS_LIBS) \ + $(GNUTLS_LIBS) \ $(LIBNL_LIBS) \ $(LIBXML_LIBS) \ + $(LIBRT_LIBS) \ $(NULL) libvirt_driver_qemu_impl_la_SOURCES = $(QEMU_DRIVER_SOURCES) @@ -2286,6 +2288,7 @@ libvirt_setuid_rpc_client_la_LDFLAGS = \ $(AM_LDFLAGS) \ $(LIBXML_LIBS) \ $(SECDRIVER_LIBS) \ + $(LIBRT_LIBS) \ $(NULL) libvirt_setuid_rpc_client_la_CFLAGS = \ -DLIBVIRT_SETUID_RPC_CLIENT \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d57bf5b..e25ef6b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2089,6 +2089,30 @@ sexpr_u64; string2sexpr; +# util/virshm.h +virShmBuildPath; +virShmCreate; +virShmObjectFindByName; +virShmObjectFree; +virShmObjectGetName; +virShmObjectGetOtherCreate; +virShmObjectGetShareable; +virShmObjectGetSize; +virShmObjectGetType; +virShmObjectGetUsedDomainNumber; +virShmObjectGetUsedDriverName; +virShmObjectListAdd; +virShmObjectListDel; +virShmObjectListGetDefault; +virShmObjectListGetStateFilePath; +virShmObjectNew; +virShmObjectRemoveStateFile; +virShmObjectSaveState; +virShmOpen; +virShmRemoveUsedDomain; +virShmSetUsedDomain; +virShmUnlink; + # util/virsocketaddr.h virSocketAddrBroadcast; virSocketAddrBroadcastByPrefix; diff --git a/src/util/virshm.c b/src/util/virshm.c new file mode 100644 index 0000000..bec9dd2 --- /dev/null +++ b/src/util/virshm.c @@ -0,0 +1,739 @@ +/* + * virshm.c: helper API for POSIX share memory + * + * Copyright (C) 2015 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/>. + * + */ + +#include <config.h> + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef HAVE_SHM_OPEN +# include <sys/mman.h> +#endif + +#include "virshm.h" +#include "virxml.h" +#include "virbuffer.h" +#include "virerror.h" +#include "virstring.h" +#include "virlog.h" +#include "virutil.h" +#include "viralloc.h" +#include "virfile.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.shm"); + +#define SHMEM_STATE_DIR LOCALSTATEDIR "/run/libvirt/shmem" + +VIR_ENUM_IMPL(virShmObject, VIR_SHM_TYPE_LAST, + "shm", + "server"); + +struct _virShmObject { + char *name; /* shmem object name */ + int type; /* shmem object type */ + unsigned long long size; /* size of shmem object */ + char *path; /* shmem path */ + bool othercreate; /* a bool parameter record if the shm is created by libvirt */ + bool shareable; /* a bool parameter record if the shm is shared between domains */ + + char *drvname; /* which driver */ + char **domains; /* domain(s) using this shm */ + size_t ndomains; /* number of useds */ +}; + +struct _virShmObjectList { + virObjectLockable parent; + char *stateDir; + size_t nshmobjs; + virShmObjectPtr *shmobjs; +}; + +static virClassPtr virShmObjectListClass; + +static virShmObjectListPtr mainlist; /* global shm object list */ + +static void virShmObjectListDispose(void *obj); + +static int +virShmObjectListLoadState(virShmObjectPtr *shmobj, + const char *stateDir, + const char *name) +{ + char *stateFile = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + virShmObjectPtr tmpshm; + xmlNodePtr *usagenode = NULL; + int ret = -1; + char *drivername = NULL; + char *shmtype = NULL; + int nusages; + + if (VIR_ALLOC(tmpshm) < 0) + return -1; + + if (!(stateFile = virFileBuildPath(stateDir, name, ".xml"))) + goto cleanup; + + if (!(xml = virXMLParseFileCtxt(stateFile, &ctxt))) + goto cleanup; + + tmpshm->name = virXPathString("string(./name)", ctxt); + if (!tmpshm->name) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("shmem missing name attribute")); + goto cleanup; + } + + shmtype = virXPathString("string(./type)", ctxt); + if (!shmtype) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("shmem missing type attribute")); + goto cleanup; + } + if ((tmpshm->type = virShmObjectTypeFromString(shmtype)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid shmem object type %s"), shmtype); + goto cleanup; + } + + if (virXPathULongLong("string(./size)", ctxt, &tmpshm->size) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("shmem missing or have invalid size attribute")); + goto cleanup; + } + + tmpshm->path = virXPathString("string(./path)", ctxt); + if (virXPathBoolean("boolean(./othercreate)", ctxt)) + tmpshm->othercreate = true; + + if (virXPathBoolean("boolean(./shareable)", ctxt)) + tmpshm->shareable = true; + + if (!(drivername = virXPathString("string(./driver)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("shmem usage element missing driver attribute")); + goto cleanup; + } + nusages = virXPathNodeSet("./domain", ctxt, &usagenode); + if (nusages < 0) + goto cleanup; + + if (nusages > 0) { + size_t i; + + for (i = 0; i < nusages; i++) { + char *domainname; + xmlNodePtr save = NULL; + + save = ctxt->node; + ctxt->node = usagenode[i]; + + if (!(domainname = virXPathString("string(./@name)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("shmem domain element missing name attribute")); + goto cleanup; + } + + if (virShmSetUsedDomain(tmpshm, drivername, domainname) < 0) { + VIR_FREE(domainname); + goto cleanup; + } + VIR_FREE(domainname); + ctxt->node = save; + } + } + *shmobj = tmpshm; + tmpshm = NULL; + ret = 0; + + cleanup: + virShmObjectFree(tmpshm); + VIR_FREE(stateFile); + VIR_FREE(drivername); + VIR_FREE(shmtype); + xmlFreeDoc(xml); + xmlXPathFreeContext(ctxt); + return ret; +} + +static int +virShmObjectListLoadAllState(virShmObjectListPtr list) +{ + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(list->stateDir))) { + if (errno == ENOENT) + return 0; + virReportSystemError(errno, _("Failed to open dir '%s'"), list->stateDir); + return -1; + } + + while (virDirRead(dir, &entry, list->stateDir) > 0) { + virShmObjectPtr shmobj; + + if (entry->d_name[0] == '.' || + !virFileStripSuffix(entry->d_name, ".xml")) + continue; + if (virShmObjectListLoadState(&shmobj, list->stateDir, entry->d_name) < 0) + continue; + if (virShmObjectListAdd(list, shmobj) < 0) + continue; + } + closedir(dir); + return 0; +} + +static virShmObjectListPtr +virShmObjectListNew(void) +{ + virShmObjectListPtr list; + bool privileged = geteuid() == 0; + + if (!(list = virObjectLockableNew(virShmObjectListClass))) + return NULL; + + if (privileged) { + if (VIR_STRDUP(list->stateDir, SHMEM_STATE_DIR) < 0) + goto error; + } else { + char *rundir = NULL; + + if (!(rundir = virGetUserRuntimeDirectory())) + goto error; + + if (virAsprintf(&list->stateDir, "%s/shmem", rundir) < 0) { + VIR_FREE(rundir); + goto error; + } + VIR_FREE(rundir); + } + if (virFileMakePath(list->stateDir) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to create state dir '%s'"), + list->stateDir); + goto error; + } + if (virShmObjectListLoadAllState(list) < 0) + goto error; + + return list; + + error: + virObjectUnref(list); + return NULL; +} + +static int +virShmOnceInit(void) +{ + if (!(virShmObjectListClass = virClassNew(virClassForObjectLockable(), + "virShmObjectList", + sizeof(virShmObjectList), + virShmObjectListDispose))) + return -1; + + if (!(mainlist = virShmObjectListNew())) + return -1; + + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virShm) + +virShmObjectListPtr +virShmObjectListGetDefault(void) +{ + if (virShmInitialize() < 0) + return NULL; + + return virObjectRef(mainlist); +} + +int +virShmSetUsedDomain(virShmObjectPtr shmobj, + const char *drvname, + const char *domname) +{ + char *tmpdomain = NULL; + bool remove_drvname = false; + + if (shmobj->drvname) { + if (STRNEQ(drvname, shmobj->drvname)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot use one shmem for different driver")); + goto error; + } + } else { + if (VIR_STRDUP(shmobj->drvname, drvname) < 0) + goto error; + remove_drvname = true; + } + + if (VIR_STRDUP(tmpdomain, domname) < 0) + goto error; + + if (VIR_APPEND_ELEMENT(shmobj->domains, shmobj->ndomains, tmpdomain) < 0) + goto error; + + return 0; + + error: + if (remove_drvname) + VIR_FREE(shmobj->drvname); + VIR_FREE(tmpdomain); + return -1; +} + +void +virShmObjectFree(virShmObjectPtr shmobj) +{ + size_t i; + + if (!shmobj) + return; + + VIR_FREE(shmobj->name); + VIR_FREE(shmobj->path); + VIR_FREE(shmobj->drvname); + for (i = 0; i < shmobj->ndomains; i++) + VIR_FREE(shmobj->domains[i]); + VIR_FREE(shmobj->domains); + VIR_FREE(shmobj); +} + +virShmObjectPtr +virShmObjectNew(const char *name, + unsigned long long size, + const char *path, + int type, + bool othercreate, + bool shareable, + const char *drvname, + const char *domname) +{ + virShmObjectPtr shmobj; + + if (VIR_ALLOC(shmobj) < 0) + return NULL; + + shmobj->size = size; + shmobj->type = type; + + if (VIR_STRDUP(shmobj->name, name) < 0) + goto error; + + if (path) { + if (VIR_STRDUP(shmobj->path, path) < 0) + goto error; + } else { + VIR_FREE(shmobj->path); + } + + shmobj->othercreate = othercreate; + shmobj->shareable = shareable; + + if (virShmSetUsedDomain(shmobj, drvname, domname) < 0) + goto error; + + return shmobj; + + error: + virShmObjectFree(shmobj); + return NULL; +} + +static void +virShmObjectListDispose(void *obj) +{ + virShmObjectListPtr list = obj; + size_t i; + + for (i = 0; i < list->nshmobjs; i++) + virShmObjectFree(list->shmobjs[i]); + + VIR_FREE(list->shmobjs); +} + +int +virShmObjectSaveState(virShmObjectPtr shmobj, + const char *stateDir) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + int ret = -1; + size_t i; + char *xml = NULL; + char *stateFile = NULL; + + virBufferAddLit(&buf, "<shmem>\n"); + virBufferAdjustIndent(&buf, 2); + + virBufferAsprintf(&buf, "<name>%s</name>\n", shmobj->name); + + virBufferAsprintf(&buf, "<type>%s</type>\n", virShmObjectTypeToString(shmobj->type)); + + virBufferAsprintf(&buf, "<size>%llu</size>\n", shmobj->size); + if (shmobj->path) + virBufferAsprintf(&buf, "<path>%s</path>\n", shmobj->path); + if (shmobj->othercreate) + virBufferAddLit(&buf, "<othercreate/>\n"); + if (shmobj->shareable) + virBufferAddLit(&buf, "<shareable/>\n"); + virBufferAsprintf(&buf, "<driver>%s</driver>\n", shmobj->drvname); + for (i = 0; i < shmobj->ndomains; i++) + virBufferAsprintf(&buf, "<domain name='%s'/>\n", shmobj->domains[i]); + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</shmem>\n"); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + if (!(xml = virBufferContentAndReset(&buf))) + goto cleanup; + + if (!(stateFile = virFileBuildPath(stateDir, shmobj->name, ".xml"))) + goto cleanup; + + if (virXMLSaveFile(stateFile, NULL, NULL, xml) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(stateFile); + VIR_FREE(xml); + return ret; +} + +int +virShmRemoveUsedDomain(virShmObjectPtr shmobj, + const char *drvname, + const char *domname) +{ + size_t i; + + if (shmobj->drvname) { + if (STRNEQ(drvname, shmobj->drvname)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot use one shmem for different driver")); + return -1; + } + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get shmem object driver name")); + return -1; + } + + for (i = 0; i < shmobj->ndomains; i++) { + if (STREQ(shmobj->domains[i], domname)) { + VIR_FREE(shmobj->domains[i]); + VIR_DELETE_ELEMENT(shmobj->domains, i, shmobj->ndomains); + return 0; + } + } + + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find domname in shmem object domain list")); + return -1; +} + +const char * +virShmObjectListGetStateFilePath(virShmObjectListPtr list) +{ + return list->stateDir; +} + +int +virShmObjectRemoveStateFile(virShmObjectListPtr list, + const char *name) +{ + char *stateFile = NULL; + + if (!(stateFile = virFileBuildPath(list->stateDir, name, ".xml"))) + return -1; + unlink(stateFile); + VIR_FREE(stateFile); + return 0; +} + +int +virShmObjectListDel(virShmObjectListPtr list, + virShmObjectPtr shmobj) +{ + size_t i; + + for (i = 0; i < list->nshmobjs; i++) { + virShmObjectPtr tmp = list->shmobjs[i]; + + if (STREQ(tmp->name, shmobj->name)) { + VIR_DELETE_ELEMENT(list->shmobjs, i, list->nshmobjs); + return 0; + } + } + return -1; +} + +const char * +virShmObjectGetName(virShmObjectPtr shmobj) +{ + return shmobj->name; +} + +unsigned long long +virShmObjectGetSize(virShmObjectPtr shmobj) +{ + return shmobj->size; +} + +int +virShmObjectGetType(virShmObjectPtr shmobj) +{ + return shmobj->type; +} + +int +virShmObjectGetUsedDomainNumber(virShmObjectPtr shmobj) +{ + return shmobj->ndomains; +} + +const char * +virShmObjectGetUsedDriverName(virShmObjectPtr shmobj) +{ + return shmobj->drvname; +} + +bool +virShmObjectGetOtherCreate(virShmObjectPtr shmobj) +{ + return shmobj->othercreate; +} + +bool +virShmObjectGetShareable(virShmObjectPtr shmobj) +{ + return shmobj->shareable; +} + +virShmObjectPtr +virShmObjectFindByName(virShmObjectListPtr list, + const char *name) +{ + size_t i; + + for (i = 0; i < list->nshmobjs; i++) { + if (STREQ_NULLABLE(list->shmobjs[i]->name, name)) + return list->shmobjs[i]; + } + + return NULL; +} + +int +virShmObjectListAdd(virShmObjectListPtr list, + virShmObjectPtr shmobj) +{ + if (virShmObjectFindByName(list, shmobj->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("A share memory object named '%s' already exists"), + shmobj->name); + return -1; + } + + return VIR_APPEND_ELEMENT(list->shmobjs, list->nshmobjs, shmobj); +} + +#ifdef HAVE_SHM_OPEN +int +virShmOpen(const char *name, + unsigned long long size, + mode_t mode) +{ + int fd = -1; + struct stat sb; + + if ((fd = shm_open(name, O_RDWR, mode)) < 0) { + virReportSystemError(errno, + _("Unable to open shared memory" + " objects '%s'"), + name); + return -1; + } + + if (fstat(fd, &sb) < 0) { + virReportSystemError(errno, + _("cannot stat file '%s'"), name); + goto error; + } + + if (sb.st_size != size) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("already exist shared memory object" + " size %ju is not equal the required size %llu"), + (uintmax_t)sb.st_size, size); + goto error; + } + + return fd; + + error: + VIR_FORCE_CLOSE(fd); + return -1; +} + +int +virShmCreate(const char *name, + unsigned long long size, + bool outerr, + bool *othercreate, + mode_t mode) +{ + int fd = -1; + + fd = shm_open(name, O_RDWR|O_CREAT|O_EXCL, mode); + + if (fd > 0) { + if (ftruncate(fd, size) != 0) { + virReportSystemError(errno, + _("Unable to truncate" + " file descriptor to %llu"), + size); + ignore_value(shm_unlink(name)); + VIR_FORCE_CLOSE(fd); + return -1; + } + } else if (errno == EEXIST) { + if (outerr) { + virReportSystemError(errno, + _("shared memory objects" + " '%s' is already exist"), + name); + return -1; + } + + VIR_WARN("shared memory objects '%s' is already exist", name); + *othercreate = true; + + return virShmOpen(name, size, mode); + } else { + virReportSystemError(errno, + _("Unable to create shared memory" + " objects '%s'"), + name); + return -1; + } + + return fd; +} + +int +virShmUnlink(const char *name) +{ + int ret; + + if ((ret = shm_unlink(name)) < 0) { + virReportSystemError(errno, + _("Unable to delete shared memory" + " objects '%s'"), + name); + } + return ret; +} + +# if defined(__linux__) +# define SHM_DEFAULT_PATH "/dev/shm" + +int +virShmBuildPath(const char *name, char **path) +{ + + if (virAsprintf(path, "%s/%s", SHM_DEFAULT_PATH, name) < 0) + return -1; + + if (!virFileExists(*path)) { + virReportSystemError(errno, + _("could not access %s"), + *path); + VIR_FREE(*path); + return -1; + } + return 0; +} + +# else + +int +virShmBuildPath(const char *name ATTRIBUTE_UNUSED, + char **path ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Cannot get share memory object path on this platform")); + return -1; +} + +# endif +#else /* !HAVE_SHM_OPEN */ +int +virShmOpen(const char *name ATTRIBUTE_UNUSED, + unsigned long long size ATTRIBUTE_UNUSED, + mode_t mode ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Cannot open share memory object on this platform")); + return -1; +} + +int +virShmCreate(const char *name ATTRIBUTE_UNUSED, + unsigned long long size ATTRIBUTE_UNUSED, + bool outerr ATTRIBUTE_UNUSED, + bool *othercreate ATTRIBUTE_UNUSED, + mode_t mode ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Cannot create share memory object on this platform")); + return -1; +} + +int +virShmUnlink(const char *name ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Cannot unlink share memory object on this platform")); + return -1; +} + +int +virShmBuildPath(const char *name ATTRIBUTE_UNUSED, + char **path ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Cannot get share memory object path on this platform")); + return -1; +} + +#endif /* !HAVE_SHM_OPEN */ diff --git a/src/util/virshm.h b/src/util/virshm.h new file mode 100644 index 0000000..ad94fd4 --- /dev/null +++ b/src/util/virshm.h @@ -0,0 +1,92 @@ +/* + * virshm.h: helper API for POSIX share memory + * + * Copyright (C) 2015 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/>. + * + */ + +#ifndef __VIR_SHM_H__ +# define __VIR_SHM_H__ + +# include "internal.h" +# include "virutil.h" +# include "virobject.h" + +typedef enum { + VIR_SHM_TYPE_SHM = 0, + VIR_SHM_TYPE_SERVER, + + VIR_SHM_TYPE_LAST +} virShmObjectType; + +VIR_ENUM_DECL(virShmObject); + +typedef struct _virShmObject virShmObject; +typedef virShmObject *virShmObjectPtr; +typedef struct _virShmObjectList virShmObjectList; +typedef virShmObjectList *virShmObjectListPtr; + +virShmObjectListPtr virShmObjectListGetDefault(void); + +int virShmSetUsedDomain(virShmObjectPtr shmobj, + const char *drvname, + const char *domname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +int virShmRemoveUsedDomain(virShmObjectPtr shmobj, + const char *drvname, + const char *domname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +void virShmObjectFree(virShmObjectPtr shmobj); +virShmObjectPtr virShmObjectNew(const char *name, unsigned long long size, + const char *path, int type, bool othercreate, + bool shareable, const char *drvname, const char *domname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(7) ATTRIBUTE_NONNULL(8); +int virShmObjectSaveState(virShmObjectPtr shmobj, + const char *stateDir) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +const char * virShmObjectListGetStateFilePath(virShmObjectListPtr list); +int virShmObjectRemoveStateFile(virShmObjectListPtr list, + const char *name) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int virShmObjectListAdd(virShmObjectListPtr list, + virShmObjectPtr shmobj) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int virShmObjectListDel(virShmObjectListPtr list, + virShmObjectPtr shmobj) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +virShmObjectPtr virShmObjectFindByName(virShmObjectListPtr list, + const char *name) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +const char * virShmObjectGetName(virShmObjectPtr shmobj); +unsigned long long virShmObjectGetSize(virShmObjectPtr shmobj); +int virShmObjectGetType(virShmObjectPtr shmobj); +int virShmObjectGetUsedDomainNumber(virShmObjectPtr shmobj); +const char * virShmObjectGetUsedDriverName(virShmObjectPtr shmobj); +bool virShmObjectGetOtherCreate(virShmObjectPtr shmobj); +bool virShmObjectGetShareable(virShmObjectPtr shmobj); + +int virShmCreate(const char *name, unsigned long long size, + bool outerr, bool *othercreate, mode_t mode) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4); +int virShmOpen(const char *name, + unsigned long long size, + mode_t mode) + ATTRIBUTE_NONNULL(1); +int virShmUnlink(const char *name); +int virShmBuildPath(const char *name, char **path) + ATTRIBUTE_NONNULL(2); +#endif /* __VIR_SHM_H__ */ -- 1.8.3.1

A new api to help set/restore the shmem device label. Signed-off-by: Luyao Huang <lhuang@redhat.com> --- src/libvirt_private.syms | 2 + src/security/security_dac.c | 100 ++++++++++++++++++++++++++++++++++++++++ src/security/security_driver.h | 9 ++++ src/security/security_manager.c | 35 ++++++++++++++ src/security/security_manager.h | 6 +++ src/security/security_nop.c | 19 ++++++++ src/security/security_selinux.c | 97 ++++++++++++++++++++++++++++++++++++++ src/security/security_stack.c | 39 ++++++++++++++++ 8 files changed, 307 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e25ef6b..620bd17 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1046,6 +1046,7 @@ virSecurityManagerRestoreDiskLabel; virSecurityManagerRestoreHostdevLabel; virSecurityManagerRestoreImageLabel; virSecurityManagerRestoreSavedStateLabel; +virSecurityManagerRestoreShmemLabel; virSecurityManagerSetAllLabel; virSecurityManagerSetChildProcessLabel; virSecurityManagerSetDaemonSocketLabel; @@ -1056,6 +1057,7 @@ virSecurityManagerSetImageFDLabel; virSecurityManagerSetImageLabel; virSecurityManagerSetProcessLabel; virSecurityManagerSetSavedStateLabel; +virSecurityManagerSetShmemLabel; virSecurityManagerSetSocketLabel; virSecurityManagerSetTapFDLabel; virSecurityManagerStackAddNested; diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 864d75b..a6b4035 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -39,6 +39,7 @@ #include "virstoragefile.h" #include "virstring.h" #include "virutil.h" +#include "virshm.h" #define VIR_FROM_THIS VIR_FROM_SECURITY @@ -930,6 +931,94 @@ virSecurityDACRestoreSecurityTPMFileLabel(virSecurityManagerPtr mgr, static int +virSecurityDACSetShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainShmemDefPtr shmem) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityLabelDefPtr seclabel = NULL; + virSecurityDeviceLabelDefPtr shmem_seclabel = NULL; + char *tmppath = NULL; + uid_t user; + gid_t group; + + if (!priv->dynamicOwnership) + return 0; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); + if (seclabel && !seclabel->relabel) + return 0; + + shmem_seclabel = virDomainShmemDefGetSecurityLabelDef(shmem, + SECURITY_DAC_NAME); + if (shmem_seclabel && !shmem_seclabel->relabel) + return 0; + + if (shmem_seclabel && shmem_seclabel->label) { + if (virParseOwnershipIds(shmem_seclabel->label, &user, &group) < 0) + return -1; + } else { + if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0) + return -1; + } + + if (shmem->server.enabled) + return virSecurityDACSetOwnership(shmem->server.chr.data.nix.path, + user, group); + + if (virShmBuildPath(shmem->name, &tmppath) < 0) + return -1; + + if (virSecurityDACSetOwnership(tmppath, user, group) < 0) { + VIR_FREE(tmppath); + return -1; + } + VIR_FREE(tmppath); + return 0; +} + + +static int +virSecurityDACRestoreShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainShmemDefPtr shmem) +{ + virSecurityDeviceLabelDefPtr shmem_seclabel = NULL; + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityLabelDefPtr secdef = NULL; + char *tmppath = NULL; + + if (!priv->dynamicOwnership) + return 0; + + if (shmem->shareable) + return 0; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); + if (secdef && !secdef->relabel) + return 0; + + shmem_seclabel = virDomainShmemDefGetSecurityLabelDef(shmem, SECURITY_DAC_NAME); + + if (shmem_seclabel && !shmem_seclabel->relabel) + return 0; + + if (shmem->server.enabled) + return virSecurityDACRestoreChardevLabel(mgr, def, NULL, &shmem->server.chr); + + if (virShmBuildPath(shmem->name, &tmppath) < 0) + return -1; + + if (virSecurityDACRestoreSecurityFileLabel(tmppath) < 0) { + VIR_FREE(tmppath); + return -1; + } + VIR_FREE(tmppath); + return 0; +} + + +static int virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, bool migrated) @@ -961,6 +1050,10 @@ virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr, migrated) < 0) rc = -1; } + for (i = 0; i < def->nshmems; i++) { + if (virSecurityDACRestoreShmemLabel(mgr, def, def->shmems[i])) + rc = -1; + } if (virDomainChrDefForeach(def, false, @@ -1038,6 +1131,10 @@ virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr, NULL) < 0) return -1; } + for (i = 0; i < def->nshmems; i++) { + if (virSecurityDACSetShmemLabel(mgr, def, def->shmems[i])) + return -1; + } if (virDomainChrDefForeach(def, true, @@ -1461,4 +1558,7 @@ virSecurityDriver virSecurityDriverDAC = { .getBaseLabel = virSecurityDACGetBaseLabel, .domainSetDirLabel = virSecurityDACDomainSetDirLabel, + + .domainSetSecurityShmemLabel = virSecurityDACSetShmemLabel, + .domainRestoreSecurityShmemLabel = virSecurityDACRestoreShmemLabel, }; diff --git a/src/security/security_driver.h b/src/security/security_driver.h index 784b0de..9fab688 100644 --- a/src/security/security_driver.h +++ b/src/security/security_driver.h @@ -121,6 +121,12 @@ typedef int (*virSecurityDomainRestoreImageLabel) (virSecurityManagerPtr mgr, typedef int (*virSecurityDomainSetDirLabel) (virSecurityManagerPtr mgr, virDomainDefPtr def, const char *path); +typedef int (*virSecurityDomainSetShmemLabel) (virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainShmemDefPtr shmem); +typedef int (*virSecurityDomainRestoreShmemLabel) (virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainShmemDefPtr shmem); struct _virSecurityDriver { @@ -173,6 +179,9 @@ struct _virSecurityDriver { virSecurityDriverGetBaseLabel getBaseLabel; virSecurityDomainSetDirLabel domainSetDirLabel; + + virSecurityDomainSetShmemLabel domainSetSecurityShmemLabel; + virSecurityDomainRestoreShmemLabel domainRestoreSecurityShmemLabel; }; virSecurityDriverPtr virSecurityDriverLookup(const char *name, diff --git a/src/security/security_manager.c b/src/security/security_manager.c index 1098558..fec73ec 100644 --- a/src/security/security_manager.c +++ b/src/security/security_manager.c @@ -1008,3 +1008,38 @@ virSecurityManagerDomainSetDirLabel(virSecurityManagerPtr mgr, return 0; } + +int +virSecurityManagerRestoreShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr vm, + virDomainShmemDefPtr shmem) +{ + if (mgr->drv->domainRestoreSecurityShmemLabel) { + int ret; + virObjectLock(mgr); + ret = mgr->drv->domainRestoreSecurityShmemLabel(mgr, vm, shmem); + virObjectUnlock(mgr); + return ret; + } + + virReportUnsupportedError(); + return -1; +} + + +int +virSecurityManagerSetShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr vm, + virDomainShmemDefPtr shmem) +{ + if (mgr->drv->domainSetSecurityShmemLabel) { + int ret; + virObjectLock(mgr); + ret = mgr->drv->domainSetSecurityShmemLabel(mgr, vm, shmem); + virObjectUnlock(mgr); + return ret; + } + + virReportUnsupportedError(); + return -1; +} diff --git a/src/security/security_manager.h b/src/security/security_manager.h index 78f34a0..c12e6e4 100644 --- a/src/security/security_manager.h +++ b/src/security/security_manager.h @@ -149,6 +149,12 @@ int virSecurityManagerSetImageLabel(virSecurityManagerPtr mgr, int virSecurityManagerRestoreImageLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, virStorageSourcePtr src); +int virSecurityManagerRestoreShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr vm, + virDomainShmemDefPtr shmem); +int virSecurityManagerSetShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr vm, + virDomainShmemDefPtr shmem); int virSecurityManagerDomainSetDirLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, diff --git a/src/security/security_nop.c b/src/security/security_nop.c index 951125d..fd786d7 100644 --- a/src/security/security_nop.c +++ b/src/security/security_nop.c @@ -236,6 +236,22 @@ virSecurityDomainSetImageLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, return 0; } +static int +virSecuritySetShmemLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainShmemDefPtr shmem ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityRestoreShmemLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainShmemDefPtr shmem ATTRIBUTE_UNUSED) +{ + return 0; +} + virSecurityDriver virSecurityDriverNop = { .privateDataLen = 0, @@ -282,4 +298,7 @@ virSecurityDriver virSecurityDriverNop = { .domainGetSecurityMountOptions = virSecurityDomainGetMountOptionsNop, .getBaseLabel = virSecurityGetBaseLabel, + + .domainSetSecurityShmemLabel = virSecuritySetShmemLabel, + .domainRestoreSecurityShmemLabel = virSecurityRestoreShmemLabel, }; diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index d2e5aa2..02495f8 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -46,6 +46,7 @@ #include "virconf.h" #include "virtpm.h" #include "virstring.h" +#include "virshm.h" #define VIR_FROM_THIS VIR_FROM_SECURITY @@ -1888,6 +1889,46 @@ virSecuritySELinuxRestoreSecuritySmartcardCallback(virDomainDefPtr def, } +static int +virSecuritySELinuxRestoreShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainShmemDefPtr shmem) +{ + virSecurityLabelDefPtr seclabel; + virSecurityDeviceLabelDefPtr shmem_seclabel = NULL; + + if (shmem->shareable) + return 0; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (!seclabel || !seclabel->relabel) + return 0; + + shmem_seclabel = virDomainShmemDefGetSecurityLabelDef(shmem, + SECURITY_SELINUX_NAME); + + if (shmem_seclabel && !shmem_seclabel->relabel) + return 0; + + if (shmem->server.enabled) { + return virSecuritySELinuxRestoreSecurityFileLabel(mgr, + shmem->server.chr.data.nix.path); + } else { + char *tmppath = NULL; + + if (virShmBuildPath(shmem->name, &tmppath) < 0) + return -1; + + if (virSecuritySELinuxRestoreSecurityFileLabel(mgr, tmppath) < 0) { + VIR_FREE(tmppath); + return -1; + } + VIR_FREE(tmppath); + return 0; + } +} + + static const char * virSecuritySELinuxGetBaseLabel(virSecurityManagerPtr mgr, int virtType) { @@ -1936,6 +1977,10 @@ virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr, migrated) < 0) rc = -1; } + for (i = 0; i < def->nshmems; i++) { + if (virSecuritySELinuxRestoreShmemLabel(mgr, def, def->shmems[i]) < 0) + rc = -1; + } if (virDomainChrDefForeach(def, false, @@ -2284,6 +2329,49 @@ virSecuritySELinuxSetSecuritySmartcardCallback(virDomainDefPtr def, static int +virSecuritySELinuxSetShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainShmemDefPtr shmem) +{ + virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); + char *imagelabel = NULL; + virSecurityLabelDefPtr seclabel; + virSecurityDeviceLabelDefPtr shmem_seclabel = NULL; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (!seclabel || !seclabel->relabel) + return 0; + + shmem_seclabel = virDomainShmemDefGetSecurityLabelDef(shmem, SECURITY_SELINUX_NAME); + + if (shmem_seclabel && !shmem_seclabel->relabel) + return 0; + + if (shmem_seclabel) + imagelabel = shmem_seclabel->label; + if (!imagelabel) + imagelabel = shmem->shareable ? data->file_context : seclabel->imagelabel; + + if (shmem->server.enabled) { + return virSecuritySELinuxSetFilecon(shmem->server.chr.data.nix.path, + imagelabel); + } else { + char *tmppath = NULL; + + if (virShmBuildPath(shmem->name, &tmppath) < 0) + return -1; + + if (virSecuritySELinuxSetFilecon(tmppath, imagelabel) < 0) { + VIR_FREE(tmppath); + return -1; + } + VIR_FREE(tmppath); + return 0; + } +} + + +static int virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, const char *stdin_path) @@ -2318,6 +2406,12 @@ virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, NULL) < 0) return -1; } + + for (i = 0; i < def->nshmems; i++) { + if (virSecuritySELinuxSetShmemLabel(mgr, def, def->shmems[i]) < 0) + return -1; + } + if (def->tpm) { if (virSecuritySELinuxSetSecurityTPMFileLabel(mgr, def, def->tpm) < 0) @@ -2565,4 +2659,7 @@ virSecurityDriver virSecurityDriverSELinux = { .getBaseLabel = virSecuritySELinuxGetBaseLabel, .domainSetDirLabel = virSecuritySELinuxDomainSetDirLabel, + + .domainSetSecurityShmemLabel = virSecuritySELinuxSetShmemLabel, + .domainRestoreSecurityShmemLabel = virSecuritySELinuxRestoreShmemLabel, }; diff --git a/src/security/security_stack.c b/src/security/security_stack.c index 8d9560d..cecde88 100644 --- a/src/security/security_stack.c +++ b/src/security/security_stack.c @@ -617,6 +617,42 @@ virSecurityStackDomainSetDirLabel(virSecurityManagerPtr mgr, return rc; } +static int +virSecurityStackSetShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr vm, + virDomainShmemDefPtr shmem) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; + int rc = 0; + + for (; item; item = item->next) { + if (virSecurityManagerSetShmemLabel(item->securityManager, + vm, shmem) < 0) + rc = -1; + } + + return rc; +} + +static int +virSecurityStackRestoreShmemLabel(virSecurityManagerPtr mgr, + virDomainDefPtr vm, + virDomainShmemDefPtr shmem) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityStackItemPtr item = priv->itemsHead; + int rc = 0; + + for (; item; item = item->next) { + if (virSecurityManagerRestoreShmemLabel(item->securityManager, + vm, shmem) < 0) + rc = -1; + } + + return rc; +} + virSecurityDriver virSecurityDriverStack = { .privateDataLen = sizeof(virSecurityStackData), .name = "stack", @@ -668,4 +704,7 @@ virSecurityDriver virSecurityDriverStack = { .getBaseLabel = virSecurityStackGetBaseLabel, .domainSetDirLabel = virSecurityStackDomainSetDirLabel, + + .domainSetSecurityShmemLabel = virSecurityStackSetShmemLabel, + .domainRestoreSecurityShmemLabel = virSecurityStackRestoreShmemLabel, }; -- 1.8.3.1

Signed-off-by: Luyao Huang <lhuang@redhat.com> --- src/qemu/qemu_conf.h | 3 + src/qemu/qemu_driver.c | 4 ++ src/qemu/qemu_process.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index ed9cd46..67a5c61 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -46,6 +46,7 @@ # include "virclosecallbacks.h" # include "virhostdev.h" # include "virfile.h" +# include "virshm.h" # ifdef CPU_SETSIZE /* Linux */ # define QEMUD_CPUMASK_LEN CPU_SETSIZE @@ -235,6 +236,8 @@ struct _virQEMUDriver { /* Immutable pointer. Unsafe APIs. XXX */ virHashTablePtr sharedDevices; + virShmObjectListPtr shmlist; + /* Immutable pointer, self-locking APIs */ virPortAllocatorPtr remotePorts; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b263ce0..f698ef8 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -776,6 +776,9 @@ qemuStateInitialize(bool privileged, if (qemuMigrationErrorInit(qemu_driver) < 0) goto error; + if (!(qemu_driver->shmlist = virShmObjectListGetDefault())) + goto error; + if (privileged) { char *channeldir; @@ -1085,6 +1088,7 @@ qemuStateCleanup(void) virObjectUnref(qemu_driver->config); virObjectUnref(qemu_driver->hostdevMgr); virHashFree(qemu_driver->sharedDevices); + virObjectUnref(qemu_driver->shmlist); virObjectUnref(qemu_driver->caps); virQEMUCapsCacheFree(qemu_driver->qemuCapsCache); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f7eb2b6..7b266e9 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4323,6 +4323,153 @@ qemuPrepareNVRAM(virQEMUDriverConfigPtr cfg, } +static int +qemuPrepareShmemDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr shmem) +{ + int ret = -1; + virShmObjectPtr tmp; + virShmObjectListPtr list = driver->shmlist; + bool othercreate = false; + char *path = NULL; + bool teardownshm = false; + int type, fd; + + virObjectLock(list); + + if ((tmp = virShmObjectFindByName(list, shmem->name))) { + if (shmem->size != virShmObjectGetSize(tmp)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Shmem object %s is already exists and " + "size is not equal require size"), + virShmObjectGetName(tmp)); + goto cleanup; + } + if (!shmem->shareable) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot use a already exist shmem" + " device in a private way")); + goto cleanup; + } else if (!virShmObjectGetShareable(tmp)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot use a already exist private shmem" + " device in a shareable way")); + goto cleanup; + } + + if (virShmSetUsedDomain(tmp, QEMU_DRIVER_NAME, vm->def->name) < 0) + goto cleanup; + + if (virShmObjectSaveState(tmp, virShmObjectListGetStateFilePath(list)) < 0) + goto cleanup; + + virObjectUnlock(list); + return 0; + } + + if (!shmem->server.enabled) { + if ((fd = virShmCreate(shmem->name, shmem->size, + false, &othercreate, 0600)) < 0) + goto cleanup; + VIR_FORCE_CLOSE(fd); + + if (virShmBuildPath(shmem->name, &path) < 0) + goto cleanup; + + type = VIR_SHM_TYPE_SHM; + } else { + if (!virFileExists(shmem->server.chr.data.nix.path)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Shmem device server socket is not exist")); + goto cleanup; + } else { + othercreate = true; + } + type = VIR_SHM_TYPE_SERVER; + } + teardownshm = true; + + if (!(tmp = virShmObjectNew(shmem->name, shmem->size, path, type, othercreate, + shmem->shareable, QEMU_DRIVER_NAME, vm->def->name))) + goto cleanup; + + if (virShmObjectSaveState(tmp, virShmObjectListGetStateFilePath(list)) < 0) { + virShmObjectFree(tmp); + goto cleanup; + } + + if (virShmObjectListAdd(list, tmp) < 0) { + virShmObjectFree(tmp); + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret < 0) { + if (teardownshm && !shmem->server.enabled && + !othercreate && virShmUnlink(shmem->name) < 0) + VIR_WARN("Unable to unlink shared memory object"); + } + VIR_FREE(path); + virObjectUnlock(list); + return ret; +} + + +static int +qemuCleanUpShmemDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr shmem) +{ + virShmObjectPtr tmp; + virShmObjectListPtr list = driver->shmlist; + int ret = -1; + + virObjectLock(list); + + if (!(tmp = virShmObjectFindByName(list, shmem->name))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot find share memory named '%s'"), + shmem->name); + goto cleanup; + } + if ((shmem->server.enabled && + virShmObjectGetType(tmp) != VIR_SHM_TYPE_SERVER) || + (!shmem->server.enabled && + virShmObjectGetType(tmp) != VIR_SHM_TYPE_SHM)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Shmem object and shmem device type is not equal")); + goto cleanup; + } + + if (virShmRemoveUsedDomain(tmp, QEMU_DRIVER_NAME, vm->def->name) < 0) + goto cleanup; + + if (virShmObjectSaveState(tmp, virShmObjectListGetStateFilePath(list)) < 0) + goto cleanup; + + if (virShmObjectGetUsedDomainNumber(tmp) == 0) { + if (!shmem->server.enabled) { + if (!virShmObjectGetOtherCreate(tmp) && + virShmUnlink(virShmObjectGetName(tmp)) < 0) + VIR_WARN("Unable to unlink shared memory object"); + } + + if (virShmObjectRemoveStateFile(list, virShmObjectGetName(tmp)) < 0) + goto cleanup; + virShmObjectListDel(list, tmp); + virShmObjectFree(tmp); + } + + ret = 0; + cleanup: + virObjectUnlock(list); + return ret; +} + + static void qemuLogOperation(virDomainObjPtr vm, const char *msg, @@ -4786,6 +4933,11 @@ int qemuProcessStart(virConnectPtr conn, if (cfg->clearEmulatorCapabilities) virCommandClearCaps(cmd); + for (i = 0; i < vm->def->nshmems; i++) { + if (qemuPrepareShmemDevice(driver, vm, vm->def->shmems[i]) < 0) + goto cleanup; + } + /* in case a certain disk is desirous of CAP_SYS_RAWIO, add this */ for (i = 0; i < vm->def->ndisks; i++) { virDomainDeviceDef dev; @@ -5438,6 +5590,11 @@ void qemuProcessStop(virQEMUDriverPtr driver, } } + for (i = 0; i < vm->def->nshmems; i++) { + ignore_value(qemuCleanUpShmemDevice(driver, vm, + vm->def->shmems[i])); + } + vm->taint = 0; vm->pid = -1; virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason); -- 1.8.3.1
participants (1)
-
Luyao Huang