[libvirt] [RFC] passing standard input to host bootloaders
by Fabian Freyer
Hello list,
Some host boot loaders, e.g. grub-bhyve when using the bhyve
driver, take commands on stdin. While there is the
<bootloader_args> tag to set arbitrary command line tags,
there is no <bootloader_stdin> or similar to provide standard
input to the boot loader.
Typical input could be something along the lines of e.g. the
following grub commands:
kernel (cd)/path/to/vmlinuz some-cmdline
initrd (root)/path/to/host/initrd
boot
Using e.g. the (root) device is especially useful on the bhyve
driver when creating diskless VMs.
Before I start implementing this, I’d appreciate some feedback
on the following two points:
1) should this be an attr on the <bootloader> tag, e.g.
<bootloader_stdin=“/path/to/file”>/path/to/bootloader</bootloader>
or rather, as there already exists a <bootloader_args> tag
a separate <bootloader_stdin> tag?
2) should the input be passed verbatim in the domain def,
e.g. using a CDATA block or from a file? Should this be
user-specified, e.g. in an attr?
<bootloader_stdin from=“/path/to/file”/>
<bootloader_stdin>
<![CDATA[
kernel (host)/path/to/kernel with cmdline
initrd (host)/path/to/ramdisk
boot
]]>
</bootloader_stdin>
Regards,
Fabian
6 years, 6 months
[libvirt] [PATCH] Add function that raises error if domain is not active
by Clementine Hayat
Add a function named virDomainObjCheckIsActive in src/conf/domain_conf.c.
It calls virDomainObjIsActive, raises error and returns.
There is a lot of occurence of this pattern and it will save 3 lines on
each call. Knowing that there is over 100 occurences, it will remove 300
lines from the code base.
Signed-off-by: Clementine Hayat <clem(a)lse.epita.fr>
---
Patch proposed for gsoc2018.
src/conf/domain_conf.c | 11 +++++
src/conf/domain_conf.h | 2 +
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 96 +++++++++-------------------------------
4 files changed, 34 insertions(+), 76 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index d23182f18..86d28c26a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -6003,6 +6003,17 @@ virDomainDefValidate(virDomainDefPtr def,
return 0;
}
+int
+virDomainObjCheckIsActive(virDomainObjPtr dom)
+{
+ if (!virDomainObjIsActive(dom)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ return -1;
+ }
+ return 0;
+}
+
/**
* virDomainDeviceLoadparmIsValid
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index bbaa24137..8de4c4145 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2722,6 +2722,8 @@ virDomainObjIsActive(virDomainObjPtr dom)
return dom->def->id != -1;
}
+int virDomainObjCheckIsActive(virDomainObjPtr dom);
+
int virDomainDefSetVcpusMax(virDomainDefPtr def,
unsigned int vcpus,
virDomainXMLOptionPtr xmlopt);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index cab324c4d..d90df3583 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -474,6 +474,7 @@ virDomainNostateReasonTypeFromString;
virDomainNostateReasonTypeToString;
virDomainObjAssignDef;
virDomainObjBroadcast;
+virDomainObjCheckIsActive;
virDomainObjCopyPersistentDef;
virDomainObjEndAPI;
virDomainObjFormat;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index fcd79bd71..22cc9bddb 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3537,11 +3537,8 @@ qemuDomainSaveFlags(virDomainPtr dom, const char *path, const char *dxml,
if (virDomainSaveFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto cleanup;
- }
ret = qemuDomainSaveInternal(driver, vm, path, compressed,
compressedpath, dxml, flags);
@@ -3595,11 +3592,9 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags)
if (virDomainManagedSaveEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto cleanup;
- }
+
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot do managed save for transient domain"));
@@ -3939,11 +3934,8 @@ qemuDomainCoreDumpWithFormat(virDomainPtr dom,
VIR_DOMAIN_JOB_OPERATION_DUMP) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
priv = vm->privateData;
priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP;
@@ -4054,11 +4046,8 @@ qemuDomainScreenshot(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
/* Well, even if qemu allows multiple graphic cards, heads, whatever,
* screenshot command does not */
@@ -4165,11 +4154,8 @@ processWatchdogEvent(virQEMUDriverPtr driver,
goto cleanup;
}
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
flags |= cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0;
if ((ret = doCoreDump(driver, vm, dumpfile, flags,
@@ -10841,11 +10827,8 @@ qemuDomainBlockResize(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
if (!(disk = virDomainDiskByName(vm->def, path, false))) {
virReportError(VIR_ERR_INVALID_ARG,
@@ -11001,11 +10984,8 @@ qemuDomainBlockStats(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
if (qemuDomainBlocksStatsGather(driver, vm, path, &blockstats) < 0)
goto endjob;
@@ -11058,11 +11038,8 @@ qemuDomainBlockStatsFlags(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
if ((nstats = qemuDomainBlocksStatsGather(driver, vm, path,
&blockstats)) < 0)
@@ -11128,11 +11105,8 @@ qemuDomainInterfaceStats(virDomainPtr dom,
if (virDomainInterfaceStatsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto cleanup;
- }
if (!(net = virDomainNetFind(vm->def, device)))
goto cleanup;
@@ -11484,11 +11458,8 @@ qemuDomainMemoryStatsInternal(virQEMUDriverPtr driver,
int ret = -1;
long rss;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
return -1;
- }
if (vm->def->memballoon &&
vm->def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO) {
@@ -11638,11 +11609,8 @@ qemuDomainMemoryPeek(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
if (virAsprintf(&tmp, "%s/qemu.mem.XXXXXX", cfg->cacheDir) < 0)
goto endjob;
@@ -13294,11 +13262,8 @@ qemuDomainGetJobStatsInternal(virQEMUDriverPtr driver,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
return -1;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID, "%s",
- _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto cleanup;
- }
if (!priv->job.current) {
jobInfo->status = QEMU_DOMAIN_JOB_STATUS_NONE;
@@ -13426,11 +13391,8 @@ static int qemuDomainAbortJob(virDomainPtr dom)
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_ABORT) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
priv = vm->privateData;
@@ -13493,11 +13455,8 @@ qemuDomainMigrateSetMaxDowntime(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
priv = vm->privateData;
@@ -13538,11 +13497,8 @@ qemuDomainMigrateGetMaxDowntime(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
priv = vm->privateData;
qemuDomainObjEnterMonitor(driver, vm);
@@ -13591,11 +13547,8 @@ qemuDomainMigrateGetCompressionCache(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
priv = vm->privateData;
@@ -13642,11 +13595,8 @@ qemuDomainMigrateSetCompressionCache(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
priv = vm->privateData;
@@ -13704,11 +13654,8 @@ qemuDomainMigrateSetMaxSpeed(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
VIR_DEBUG("Setting migration bandwidth to %luMbs", bandwidth);
qemuDomainObjEnterMonitor(driver, vm);
@@ -13779,11 +13726,8 @@ qemuDomainMigrateStartPostCopy(virDomainPtr dom,
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0)
goto cleanup;
- if (!virDomainObjIsActive(vm)) {
- virReportError(VIR_ERR_OPERATION_INVALID, "%s",
- _("domain is not running"));
+ if (virDomainObjCheckIsActive(vm) < 0)
goto endjob;
- }
priv = vm->privateData;
--
2.17.0
6 years, 6 months
[libvirt] [dbus PATCH v3 00/20] More APIs for Domain Interface
by Katerina Koukiou
Changes from v2:
Adjusted to reviews.
Katerina Koukiou (20):
Implement Setter for Autostart property for Domain interface
Implement SchedulerType property for Domain Interface
Implement MemoryStats for Domain Interface
Implement AttachDevice method for Domain Interface
Implement DetachDevice method for Domain Interface
Implement GetJobInfo method for Domain interface
Implement AbortJob for Domain interface
Implement MigrateSetMaxDowntime method for Domain interface
Implement MigrateGetMaxDowntime method for Domain Interface
Implement ManagedSave method for Domain Interface
Implement HasManagedSaveImage method for Domain Interface
Implement ManagedSaveRemove method for Domain Interface
Implement SetVcpus method for Domain Interface
Implement GetMemoryParameters method for Domain Interface
Implement GetBlkioParameters method for domain Interface
Implement Updated property for Domain Interface
Implement MigrateGetMaxSpeed method for Domain Interface
Implement MigrateSetMaxSpeed method for Domain Interface
Implement SetMemory method for Domain Interface
Implement GetSchedulerParameters method for Domain Interface
data/org.libvirt.Domain.xml | 110 +++++++-
src/domain.c | 605 +++++++++++++++++++++++++++++++++++++++++++-
test/test_domain.py | 37 ++-
3 files changed, 747 insertions(+), 5 deletions(-)
--
2.15.0
6 years, 6 months
[libvirt] [PATCH v5 00/10] x86: Secure Encrypted Virtualization (AMD)
by Brijesh Singh
This patch series provides support for launching an encrypted guest using
AMD's new Secure Encrypted Virtualization (SEV) feature.
SEV is an extension to the AMD-V architecture which supports running
multiple VMs under the control of a hypervisor. When enabled, SEV feature
allows the memory contents of a virtual machine (VM) to be transparently
encrypted with a key unique to the guest VM.
At very high level the flow looks this:
1. mgmt tool calls virConnectGetDomainCapabilities. This returns an XML document
that includes the following
<feature>
...
<sev supported='yes'>
<cbitpos> </cbitpos>
<reduced-phys-bits> </reduced-phys-bits>
<pdh> </pdh>
<cert-chain> </cert-chain>
</feature>
If <sev> is provided then we indicate that hypervisor is capable of launching
SEV guest.
2. (optional) mgmt tool can provide the PDH and Cert-chain to guest owner in case
if guest owner wish to establish a secure connection with SEV firmware to
negotiate a key used for validating the measurement.
3. mgmt tool requests to start a guest calling virCreateXML(), passing VIR_DOMAIN_START_PAUSED.
The xml would include
<launch-security type='sev'>
<cbitpos> </cbitpos> /* the value is same as what is obtained via virConnectGetDomainCapabilities()
<reduced-phys-bits> </reduced-phys-bits> /* the value is same as what is obtained via virConnectGetDomainCapabilities()
<dh-cert> .. </dh> /* guest owners diffie-hellman key */ (optional)
<session> ..</session> /* guest owners session blob */ (optional)
<policy> ..</policy> /* guest policy */ (optional)
</launch-security>
4. Libvirt generate the QEMU cli arg to enable the SEV feature, a typical
args looks like this:
# $QEMU ..
-machine memory-encryption=sev0 \
-object sev-guest,id=sev0,dh-cert-file=<file>....
5. Libvirt generates lifecycle VIR_DOMAIN_EVENT_SUSPENDED_PAUSED event
6. mgmt tool gets the VIR_DOMAIN_EVENT_SUSPENDED_PAUSED and calls virDomainGetLaunchSecretInfo()
to retrieve the measurement of encrypted memory.
7. (optional) mgmt tool can provide the measurement value to guest owner, which can
validate the measurement and gives GO/NO-GO answer. If mgmt tool gets GO then
it resumes the guest otherwise it calls destroy() to kill the guest.
8. mgmt tool resumes the guest
TODO:
* SEV guest require to use DMA apis for the virtio devices. In order to use the DMA
apis the virtio devices must have this tag
<driver iommu=on ats=on>
It is a bit unclear to me where these changes need to go. Do we need to
modify the libvirt to automatically add these when SEV is enabled or
we ask mgmt tool to make sure that it creates XML with right tag to enable
the DMA APIs for virtio devices. I am looking for some suggestions.
Using these patches we have succesfully booted and tested a guest both with and
without SEV enabled.
SEV Firmware API spec is available at:
https://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
Changes since v4:
* add /dev/sev in shared device list
Changes since v3:
* rename QEMU_CAPS_SEV -> QEMU_CAPS_SEV_GUEST
* update caps_2.12.0.x86_64.replies to include query-sev-capabilities data
Changes since v2:
* make cbitpos, policy and reduced-phys-bits as unsigned int
* update virDomainGetLaunchSecurityInfo to accept virTypedParameterPtr *params
instead of virTypedParameterPtr params.
Changes since v1:
* rename <sev> -> <launch-security> for domain
* add more information about policy and other fields in domaincaps.html
* split the domain_conf support in two patches
* add virDomainGetLaunchInfo() to retrieve the SEV measurement
* extend virsh command to show the domain's launch security information
* add test cases to validate newly added <launch-security> element
* fix issues reported with 'make check' and 'make syntax-check'
The complete git tree is available at:
https://github.com/codomania/libvirt/tree/v5
Brijesh Singh (9):
qemu: provide support to query the SEV capability
qemu: introduce SEV feature in hypervisor capabilities
conf: introduce launch-security element in domain
qemu/cgroup: add /dev/sev in shared devices list
qemu: add support to launch SEV guest
libvirt: add new public API to get launch security info
remote: implement the remote protocol for launch security
qemu_driver: add support to launch security info
virsh: implement new command for launch security
Xiaogang Chen (1):
tests: extend tests to include sev specific tag parsing
docs/drvqemu.html.in | 1 +
docs/formatdomain.html.in | 120 +++++++++++++++++++++
docs/formatdomaincaps.html.in | 40 +++++++
docs/schemas/domaincaps.rng | 20 ++++
docs/schemas/domaincommon.rng | 39 +++++++
include/libvirt/libvirt-domain.h | 17 +++
src/conf/domain_capabilities.c | 20 ++++
src/conf/domain_capabilities.h | 14 +++
src/conf/domain_conf.c | 110 +++++++++++++++++++
src/conf/domain_conf.h | 26 +++++
src/driver-hypervisor.h | 7 ++
src/libvirt-domain.c | 48 +++++++++
src/libvirt_public.syms | 5 +
src/qemu/qemu.conf | 2 +-
src/qemu/qemu_capabilities.c | 40 +++++++
src/qemu/qemu_capabilities.h | 1 +
src/qemu/qemu_capspriv.h | 4 +
src/qemu/qemu_cgroup.c | 2 +-
src/qemu/qemu_command.c | 35 ++++++
src/qemu/qemu_driver.c | 66 ++++++++++++
src/qemu/qemu_monitor.c | 17 +++
src/qemu/qemu_monitor.h | 6 ++
src/qemu/qemu_monitor_json.c | 105 ++++++++++++++++++
src/qemu/qemu_monitor_json.h | 5 +
src/qemu/qemu_process.c | 58 ++++++++++
src/remote/remote_daemon_dispatch.c | 47 ++++++++
src/remote/remote_driver.c | 42 +++++++-
src/remote/remote_protocol.x | 20 +++-
src/remote_protocol-structs | 11 ++
tests/genericxml2xmlindata/sev.xml | 20 ++++
tests/genericxml2xmloutdata/sev.xml | 22 ++++
tests/genericxml2xmltest.c | 2 +
.../caps_2.12.0.x86_64.replies | 10 ++
tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 3 +-
tests/qemuxml2argvdata/sev.args | 24 +++++
tests/qemuxml2argvdata/sev.xml | 35 ++++++
tests/qemuxml2argvtest.c | 2 +
tests/qemuxml2xmloutdata/sev.xml | 39 +++++++
tests/qemuxml2xmltest.c | 2 +
tools/virsh-domain.c | 84 +++++++++++++++
40 files changed, 1166 insertions(+), 5 deletions(-)
create mode 100644 tests/genericxml2xmlindata/sev.xml
create mode 100644 tests/genericxml2xmloutdata/sev.xml
create mode 100644 tests/qemuxml2argvdata/sev.args
create mode 100644 tests/qemuxml2argvdata/sev.xml
create mode 100644 tests/qemuxml2xmloutdata/sev.xml
--
2.7.4
6 years, 6 months
[libvirt] [PATCH v3] qemu: add virQEMUBuildBufferEscapeComma in qemu_command.c
by Sukrit Bhatnagar
This patch adds virQEMUBuildBufferEscapeComma to properly
escape commas in user provided data fields for qemu command
line processing.
Signed-off-by: Sukrit Bhatnagar <skrtbhtngr(a)gmail.com>
---
Thank you for the helpful feedback and apologies for the delay.
Changes in v3:
virQEMUBuildBufferEscapeComma was applied to:
- src->hosts->socket in qemuBuildNetworkDriveURI
- src->path, src->configFile in qemuBuildNetworkDriveStr
- disk->blkdeviotune.group_name in qemuBuildDiskThrottling
- net->data.socket.address, net->data.socket.localaddr in
qemuBuildHostNetStr
- dev->data.file.path in qemuBuildChrChardevStr
- graphics->data.spice.rendernode in
qemuBuildGraphicsSPICECommandLine
- smartcard->data.cert.file[i], smartcard->data.cert.database in
qemuBuildSmartcardCommandLine
Changes in v2:
virQEMUBuildBufferEscapeComma was applied to:
- info->romfile in qemuBuildRomStr
- disk->vendor, disk->product in qemuBuildDriveDevStr
- fs->src->path in qemuBuildFSStr
- fs->dst in qemuBuildFSDevStr
- connect= in qemuBuildHostNetStr
- fileval handling in qemuBuildChrChardevStr
- TYPE_DEV, TYPE_PIPE handling in qemuBuildChrChardevStr
- cfg->vncTLSx509certdir in qemuBuildGraphicsVNCCommandLine
- cfg->spiceTLSx509certdir in qemuBuildGraphicsSPICECommandLine
- loader->path, loader->nvram usage in
qemuBuildDomainLoaderCommandLine
Link to v2: https://www.redhat.com/archives/libvir-list/2018-March/msg00965.html
When I tried to change src->path in qemuBuildNetworkDriveStr
for this portion
961 } else if (src->nhosts == 1) {
962 if (virAsprintf(&ret, "sheepdog:%s:%u:%s",
963 src->hosts->name, src->hosts->port,
964 src->path) < 0)
965 goto cleanup;
966 } else {
make check reported the following error.
141) QEMU XML-2-ARGV disk-drive-network-sheepdog ...
In '/home/skrtbhtngr/libvirt/tests/qemuxml2argvdata/disk-drive-network-sheepdog.args':
Offset 0
Expect [LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none /usr/bin/qemu-system-i686 -name QEMUGuest1 -S -M pc -m 214 -smp 1,sockets=1,cores=1,threads=1 -uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 -nographic -nodefaults -chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c -usb -drive file=/dev/HostVG/QEMU,,Guest,,,,1,format=raw,if=none,id=drive-ide0-0-0 -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 -drive file=sheepdog:example.org:6000:image,,with,,commas,format=raw,if=none,id=drive-virtio-disk0 -device virtio-blk-pci,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0
]
Actual [LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none /usr/bin/qemu-system-i686 -name QEMUGuest1 -S -M pc -m 214 -smp 1,sockets=1,cores=1,threads=1 -uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 -nographic -nodefaults -chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c -usb -drive file=/dev/HostVG/QEMU,,Guest,,,,1,format=raw,if=none,id=drive-ide0-0-0 -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 -drive file=sheepdog:example.org:6000:image,,,,with,,,,commas,format=raw,if=none,id=drive-virtio-disk0 -device virtio-blk-pci,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0
]
... FAILED
In disk-drive-network-sheepdog.args:
...
-drive file=sheepdog:example.org:6000:image,,with,,commas,format=raw,if=none,\
...
I was not quite sure how to handle this part. Adding
virQEMUBuildBufferEscapeComma there is escaping the twin commas
in the file name again. I have left that part unchanged.
src/qemu/qemu_command.c | 111 +++++++++++++++++++++++++-----------------------
1 file changed, 59 insertions(+), 52 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 6f76f18ab..26b36551c 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -844,14 +844,18 @@ qemuBuildNetworkDriveURI(virStorageSourcePtr src,
qemuDomainSecretInfoPtr secinfo)
{
virURIPtr uri = NULL;
- char *ret = NULL;
+ char *ret = NULL, *socket = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!(uri = qemuBlockStorageSourceGetURI(src)))
goto cleanup;
- if (src->hosts->socket &&
- virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0)
- goto cleanup;
+ if (src->hosts->socket) {
+ virQEMUBuildBufferEscapeComma(&buf, src->hosts->socket);
+ socket = virBufferContentAndReset(&buf);
+ if (virAsprintf(&uri->query, "socket=%s", socket) < 0)
+ goto cleanup;
+ }
if (qemuBuildGeneralSecinfoURI(uri, secinfo) < 0)
goto cleanup;
@@ -860,6 +864,8 @@ qemuBuildNetworkDriveURI(virStorageSourcePtr src,
cleanup:
virURIFree(uri);
+ virBufferFreeAndReset(&buf);
+
return ret;
}
@@ -868,8 +874,9 @@ static char *
qemuBuildNetworkDriveStr(virStorageSourcePtr src,
qemuDomainSecretInfoPtr secinfo)
{
- char *ret = NULL;
+ char *ret = NULL, *path = NULL, *file = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
+ virBuffer bufTemp = VIR_BUFFER_INITIALIZER;
size_t i;
switch ((virStorageNetProtocol) src->protocol) {
@@ -914,8 +921,10 @@ qemuBuildNetworkDriveStr(virStorageSourcePtr src,
goto cleanup;
}
- if (src->path)
- virBufferAsprintf(&buf, ":exportname=%s", src->path);
+ if (src->path) {
+ virBufferAddLit(&buf, ":exportname=");
+ virQEMUBuildBufferEscapeComma(&buf, src->path);
+ }
if (virBufferCheckError(&buf) < 0)
goto cleanup;
@@ -945,7 +954,9 @@ qemuBuildNetworkDriveStr(virStorageSourcePtr src,
}
if (src->nhosts == 0) {
- if (virAsprintf(&ret, "sheepdog:%s", src->path) < 0)
+ virQEMUBuildBufferEscapeComma(&bufTemp, src->path);
+ path = virBufferContentAndReset(&bufTemp);
+ if (virAsprintf(&ret, "sheepdog:%s", path) < 0)
goto cleanup;
} else if (src->nhosts == 1) {
if (virAsprintf(&ret, "sheepdog:%s:%u:%s",
@@ -967,8 +978,9 @@ qemuBuildNetworkDriveStr(virStorageSourcePtr src,
src->path);
goto cleanup;
}
-
- virBufferStrcat(&buf, "rbd:", src->volume, "/", src->path, NULL);
+ virQEMUBuildBufferEscapeComma(&bufTemp, src->path);
+ path = virBufferContentAndReset(&bufTemp);
+ virBufferStrcat(&buf, "rbd:", src->volume, "/", path, NULL);
if (src->snapshot)
virBufferEscape(&buf, '\\', ":", "@%s", src->snapshot);
@@ -994,8 +1006,11 @@ qemuBuildNetworkDriveStr(virStorageSourcePtr src,
}
}
- if (src->configFile)
- virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile);
+ if (src->configFile) {
+ virQEMUBuildBufferEscapeComma(&bufTemp, src->configFile);
+ file = virBufferContentAndReset(&bufTemp);
+ virBufferEscape(&buf, '\\', ":", ":conf=%s", file);
+ }
if (virBufferCheckError(&buf) < 0)
goto cleanup;
@@ -1022,6 +1037,7 @@ qemuBuildNetworkDriveStr(virStorageSourcePtr src,
}
cleanup:
+ virBufferFreeAndReset(&bufTemp);
virBufferFreeAndReset(&buf);
return ret;
@@ -1630,6 +1646,8 @@ qemuBuildDiskThrottling(virDomainDiskDefPtr disk,
virBufferAsprintf(buf, ",throttling." _label "=%llu", \
disk->blkdeviotune._field); \
}
+ virBuffer bufTemp = VIR_BUFFER_INITIALIZER;
+ char *name = NULL;
IOTUNE_ADD(total_bytes_sec, "bps-total");
IOTUNE_ADD(read_bytes_sec, "bps-read");
@@ -1647,8 +1665,9 @@ qemuBuildDiskThrottling(virDomainDiskDefPtr disk,
IOTUNE_ADD(size_iops_sec, "iops-size");
if (disk->blkdeviotune.group_name) {
- virBufferEscapeString(buf, ",throttling.group=%s",
- disk->blkdeviotune.group_name);
+ virQEMUBuildBufferEscapeComma(&bufTemp, disk->blkdeviotune.group_name);
+ name = virBufferContentAndReset(&bufTemp);
+ virBufferEscapeString(buf, ",throttling.group=%s", name);
}
IOTUNE_ADD(total_bytes_sec_max_length, "bps-total-max-length");
@@ -1657,6 +1676,8 @@ qemuBuildDiskThrottling(virDomainDiskDefPtr disk,
IOTUNE_ADD(total_iops_sec_max_length, "iops-total-max-length");
IOTUNE_ADD(read_iops_sec_max_length, "iops-read-max-length");
IOTUNE_ADD(write_iops_sec_max_length, "iops-write-max-length");
+
+ virBufferFreeAndReset(&bufTemp);
#undef IOTUNE_ADD
}
@@ -3651,27 +3672,25 @@ qemuBuildHostNetStr(virDomainNetDefPtr net,
break;
case VIR_DOMAIN_NET_TYPE_SERVER:
- virBufferAsprintf(&buf, "socket%clisten=%s:%d,",
- type_sep,
+ virBufferAsprintf(&buf, "socket%clisten=", type_sep);
+ virQEMUBuildBufferEscapeComma(&buf,
net->data.socket.address ? net->data.socket.address
- : "",
- net->data.socket.port);
+ : "");
+ virBufferAsprintf(&buf, ":%d,", net->data.socket.port);
break;
case VIR_DOMAIN_NET_TYPE_MCAST:
- virBufferAsprintf(&buf, "socket%cmcast=%s:%d,",
- type_sep,
- net->data.socket.address,
- net->data.socket.port);
+ virBufferAsprintf(&buf, "socket%cmcast=", type_sep);
+ virQEMUBuildBufferEscapeComma(&buf, net->data.socket.address);
+ virBufferAsprintf(&buf, ":%d,", net->data.socket.port);
break;
case VIR_DOMAIN_NET_TYPE_UDP:
- virBufferAsprintf(&buf, "socket%cudp=%s:%d,localaddr=%s:%d,",
- type_sep,
- net->data.socket.address,
- net->data.socket.port,
- net->data.socket.localaddr,
- net->data.socket.localport);
+ virBufferAsprintf(&buf, "socket%cudp=", type_sep);
+ virQEMUBuildBufferEscapeComma(&buf, net->data.socket.address);
+ virBufferAsprintf(&buf, ":%d,localaddr=", net->data.socket.port);
+ virQEMUBuildBufferEscapeComma(&buf, net->data.socket.localaddr);
+ virBufferAsprintf(&buf, ":%d,", net->data.socket.localport);
break;
case VIR_DOMAIN_NET_TYPE_USER:
@@ -4954,9 +4973,10 @@ qemuBuildChrChardevStr(virLogManagerPtr logManager,
bool chardevStdioLogd)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
+ virBuffer bufTemp = VIR_BUFFER_INITIALIZER;
bool telnet;
char *charAlias = NULL;
- char *ret = NULL;
+ char *ret = NULL, *path = NULL;
if (!(charAlias = qemuAliasChardevFromDevAlias(alias)))
goto cleanup;
@@ -4990,9 +5010,11 @@ qemuBuildChrChardevStr(virLogManagerPtr logManager,
_("append not supported in this QEMU binary"));
goto cleanup;
}
+ virQEMUBuildBufferEscapeComma(&bufTemp, dev->data.file.path);
+ path = virBufferContentAndReset(&bufTemp);
if (qemuBuildChrChardevFileStr(chardevStdioLogd ? logManager : NULL,
cmd, def, &buf,
- "path", dev->data.file.path,
+ "path", path,
"append", dev->data.file.append) < 0)
goto cleanup;
break;
@@ -8150,8 +8172,8 @@ qemuBuildGraphicsSPICECommandLine(virQEMUDriverConfigPtr cfg,
_("This QEMU doesn't support spice OpenGL rendernode"));
goto error;
}
-
- virBufferAsprintf(&opt, "rendernode=%s,", graphics->data.spice.rendernode);
+ virBufferAddLit(&opt, "rendernode=");
+ virQEMUBuildBufferEscapeComma(&opt, graphics->data.spice.rendernode);
}
}
@@ -8771,7 +8793,6 @@ qemuBuildSmartcardCommandLine(virLogManagerPtr logManager,
virDomainSmartcardDefPtr smartcard;
char *devstr;
virBuffer opt = VIR_BUFFER_INITIALIZER;
- const char *database;
const char *contAlias = NULL;
if (!def->nsmartcards)
@@ -8814,29 +8835,15 @@ qemuBuildSmartcardCommandLine(virLogManagerPtr logManager,
virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates");
for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) {
- if (strchr(smartcard->data.cert.file[i], ',')) {
- virBufferFreeAndReset(&opt);
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("invalid certificate name: %s"),
- smartcard->data.cert.file[i]);
- return -1;
- }
- virBufferAsprintf(&opt, ",cert%zu=%s", i + 1,
- smartcard->data.cert.file[i]);
+ virBufferAsprintf(&opt, ",cert%zu=", i + 1);
+ virQEMUBuildBufferEscapeComma(&opt, smartcard->data.cert.file[i]);
}
if (smartcard->data.cert.database) {
- if (strchr(smartcard->data.cert.database, ',')) {
- virBufferFreeAndReset(&opt);
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("invalid database name: %s"),
- smartcard->data.cert.database);
- return -1;
- }
- database = smartcard->data.cert.database;
+ virBufferAddLit(&opt, ",db=");
+ virQEMUBuildBufferEscapeComma(&opt, smartcard->data.cert.database);
} else {
- database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE;
+ virBufferAsprintf(&opt, ",db=%s", VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE);
}
- virBufferAsprintf(&opt, ",db=%s", database);
break;
case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
--
2.16.2
6 years, 6 months
[libvirt] [PATCHv2] Remove redundant virFileIsExecutable check
by Radostin Stoyanov
Remove unnecessary virFileIsExecutable check after virFindFileInPath.
Since commit 9ae992f, virFindFileInPath will reject non-executables.
9ae992f24353d6506f570fc9dd58355b165e4472
virFindFileInPath: only find executable non-directory
Signed-off-by: Radostin Stoyanov <rstoyanov1(a)gmail.com>
---
src/bhyve/bhyve_capabilities.c | 4 ----
src/qemu/qemu_capabilities.c | 2 +-
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/bhyve/bhyve_capabilities.c b/src/bhyve/bhyve_capabilities.c
index 381cc0de3..e13085b1d 100644
--- a/src/bhyve/bhyve_capabilities.c
+++ b/src/bhyve/bhyve_capabilities.c
@@ -179,8 +179,6 @@ virBhyveProbeGrubCaps(virBhyveGrubCapsFlags *caps)
binary = virFindFileInPath("grub-bhyve");
if (binary == NULL)
goto out;
- if (!virFileIsExecutable(binary))
- goto out;
cmd = virCommandNew(binary);
virCommandAddArg(cmd, "--help");
@@ -315,8 +313,6 @@ virBhyveProbeCaps(unsigned int *caps)
binary = virFindFileInPath("bhyve");
if (binary == NULL)
goto out;
- if (!virFileIsExecutable(binary))
- goto out;
if ((ret = bhyveProbeCapsRTC_UTC(caps, binary)))
goto out;
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 27180e850..13c6c85d8 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -653,7 +653,7 @@ virQEMUCapsFindBinary(const char *format,
ret = virFindFileInPath(binary);
VIR_FREE(binary);
- if (ret && virFileIsExecutable(ret))
+ if (ret)
goto out;
VIR_FREE(ret);
--
2.14.3
6 years, 6 months
[libvirt] [dbus PATCH v2 00/22] More APIs for Domain Interface
by Katerina Koukiou
Changes from v1:
Added some more APIs.
Katerina Koukiou (22):
Implement Setter for Autostart property for Domain interface
Implement SchedulerType property for Domain Interface
Introduce virtDBusDomainMemoryStatTypeToString helper function
Implement MemoryStats for Domain Interface
Implement AttachDevice method for Domain Interface
Implement DetachDevice method for Domain Interface
Introduce virtDBusDomainJobTypeToString function
Implement GetJobInfo method for Domain interface
Implement AbortJob for Domain interface
Implement MigrateSetMaxDowntime method for Domain interface
Implement MigrateGetMaxDowntime method for Domain Interface
Implement ManagedSave method for Domain Interface
Implement HasManagedSaveImage method for Domain Interface
Implement ManagedSaveRemove method for Domain Interface
Implement SetVcpus method for Domain Interface
Implement GetMemoryParameters method for Domain Interface
Implement GetBlkioParameters method for domain Interface
Implement Updated property for Domain Interface
Implement MigrateGetMaxSpeed method for Domain Interface
Implement MigrateSetMaxSpeed method for Domain Interface
Implement SetMemory method for Domain Interface
Implement GetSchedulerParameters method for Domain Interface
data/org.libvirt.Domain.xml | 111 +++++++-
src/domain.c | 599 +++++++++++++++++++++++++++++++++++++++++++-
test/test_domain.py | 37 ++-
3 files changed, 742 insertions(+), 5 deletions(-)
--
2.15.0
6 years, 6 months
[libvirt] [PATCH] Remove redundant virFileIsExecutable check
by Radostin Stoyanov
Remove unnecessary virFileIsExecutable check after virFindFileInPath.
Since the commit 9ae992f virFindFileInPath will reject non-executables.
9ae992f24353d6506f570fc9dd58355b165e4472
virFindFileInPath: only find executable non-directory
Signed-off-by: Radostin Stoyanov <rstoyanov1(a)gmail.com>
---
src/bhyve/bhyve_capabilities.c | 4 ----
src/qemu/qemu_capabilities.c | 2 +-
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/bhyve/bhyve_capabilities.c b/src/bhyve/bhyve_capabilities.c
index 381cc0de3..e13085b1d 100644
--- a/src/bhyve/bhyve_capabilities.c
+++ b/src/bhyve/bhyve_capabilities.c
@@ -179,8 +179,6 @@ virBhyveProbeGrubCaps(virBhyveGrubCapsFlags *caps)
binary = virFindFileInPath("grub-bhyve");
if (binary == NULL)
goto out;
- if (!virFileIsExecutable(binary))
- goto out;
cmd = virCommandNew(binary);
virCommandAddArg(cmd, "--help");
@@ -315,8 +313,6 @@ virBhyveProbeCaps(unsigned int *caps)
binary = virFindFileInPath("bhyve");
if (binary == NULL)
goto out;
- if (!virFileIsExecutable(binary))
- goto out;
if ((ret = bhyveProbeCapsRTC_UTC(caps, binary)))
goto out;
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 27180e850..5ebc72f6f 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -653,7 +653,7 @@ virQEMUCapsFindBinary(const char *format,
ret = virFindFileInPath(binary);
VIR_FREE(binary);
- if (ret && virFileIsExecutable(ret))
+ if (ret == NULL)
goto out;
VIR_FREE(ret);
--
2.14.3
6 years, 6 months
[libvirt] [PATCH] tests: remove FLAG_JSON from xml2argvtest
by Ján Tomko
Unused as of commit <1e9a083>.
Signed-off-by: Ján Tomko <jtomko(a)redhat.com>
---
Pushed as trivial.
tests/qemuxml2argvtest.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 3a328e02a2..472f7b28fe 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -265,8 +265,7 @@ static virStorageDriver fakeStorageDriver = {
typedef enum {
FLAG_EXPECT_FAILURE = 1 << 0,
FLAG_EXPECT_PARSE_ERROR = 1 << 1,
- FLAG_JSON = 1 << 2,
- FLAG_FIPS = 1 << 3,
+ FLAG_FIPS = 1 << 2,
} virQemuXML2ArgvTestFlags;
struct testInfo {
@@ -462,9 +461,6 @@ testCompareXMLToArgv(const void *data)
virSetConnectSecret(conn);
virSetConnectStorage(conn);
- if (virQEMUCapsGet(info->qemuCaps, QEMU_CAPS_MONITOR_JSON))
- flags |= FLAG_JSON;
-
if (virQEMUCapsGet(info->qemuCaps, QEMU_CAPS_ENABLE_FIPS))
flags |= FLAG_FIPS;
--
2.16.1
6 years, 6 months
[libvirt] [PATCH 0/8] Simplify & standarize more parts of driver URI handling
by Daniel P. Berrangé
The motivating goal of this series was/is to make it possible to answer
whether there is any driver registered to handle a given URI scheme
without having to call into the drivers.
Ultimately this ended up being a nice simplification and cleanup of
driver URI opening code, removing repetative logic from all drivers.
Daniel P. Berrangé (8):
xen: encourage use of xen:///system URI as preferred format
lxc: allow use of lxc:///system URI as preferred format
driver: introduce a driver method for probing default URIs
driver: allow drivers to indicate if they permit remote connections
driver: declare supported URI schemes in virConnectDriver struct
driver: ensure NULL URI isn't passed to drivers with whitelisted URIs
driver: enforce a non-NULL URI scheme
driver: ensure URI path is non-NULL to simplify drivers
docs/drvlxc.html.in | 34 ++++++-------
docs/drvxen.html.in | 14 +++---
docs/hvsupport.pl | 6 +--
docs/remote.html.in | 6 +--
docs/uri.html.in | 16 +++----
examples/lxcconvert/virt-lxc-convert | 2 +-
src/bhyve/bhyve_driver.c | 48 +++++++++----------
src/driver-hypervisor.h | 4 ++
src/driver.h | 8 ++++
src/esx/esx_driver.c | 31 +-----------
src/hyperv/hyperv_driver.c | 24 +---------
src/interface/interface_backend_netcf.c | 47 +++++++-----------
src/interface/interface_backend_udev.c | 47 +++++++-----------
src/libvirt.c | 69 +++++++++++++++++++++++---
src/libxl/libxl_driver.c | 58 +++++++++++-----------
src/lxc/lxc_driver.c | 56 ++++++++++------------
src/lxc/lxc_process.c | 4 +-
src/network/bridge_driver.c | 47 +++++++-----------
src/node_device/node_device_driver.c | 45 +++++++----------
src/node_device/node_device_hal.c | 2 +
src/node_device/node_device_udev.c | 2 +
src/nwfilter/nwfilter_driver.c | 35 +++++---------
src/openvz/openvz_driver.c | 70 +++++++++++++--------------
src/phyp/phyp_driver.c | 9 +---
src/qemu/qemu_driver.c | 85 +++++++++++++++------------------
src/remote/remote_driver.c | 74 +++++++++++++---------------
src/secret/secret_driver.c | 47 +++++++-----------
src/storage/storage_driver.c | 47 +++++++-----------
src/test/test_driver.c | 18 ++-----
src/uml/uml_driver.c | 71 +++++++++++++--------------
src/vbox/vbox_common.c | 26 ++++------
src/vbox/vbox_driver.c | 18 ++-----
src/vmware/vmware_driver.c | 29 ++++-------
src/vz/vz_driver.c | 25 ++--------
src/xenapi/xenapi_driver.c | 6 +--
tools/libvirt-guests.sysconf | 2 +-
tools/virsh.pod | 4 +-
tools/virt-login-shell.c | 2 +-
38 files changed, 489 insertions(+), 649 deletions(-)
--
2.14.3
6 years, 6 months