2005-11-10 16:12:31 +00:00
|
|
|
/*
|
2010-06-09 09:00:30 +00:00
|
|
|
* virsh.c: a shell to exercise the libvirt API
|
2005-11-10 16:12:31 +00:00
|
|
|
*
|
2015-02-19 13:16:39 +00:00
|
|
|
* Copyright (C) 2005, 2007-2015 Red Hat, Inc.
|
2005-11-10 16:12:31 +00:00
|
|
|
*
|
2012-07-27 09:39:53 +00:00
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-27 09:39:53 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2005-11-10 16:12:31 +00:00
|
|
|
*
|
|
|
|
* Daniel Veillard <veillard@redhat.com>
|
2005-12-08 14:22:52 +00:00
|
|
|
* Karel Zak <kzak@redhat.com>
|
2006-05-29 15:39:31 +00:00
|
|
|
* Daniel P. Berrange <berrange@redhat.com>
|
2005-11-10 16:12:31 +00:00
|
|
|
*/
|
|
|
|
|
2008-01-29 18:15:54 +00:00
|
|
|
#include <config.h>
|
2012-08-18 03:16:04 +00:00
|
|
|
#include "virsh.h"
|
2007-12-04 18:27:52 +00:00
|
|
|
|
2005-11-10 16:12:31 +00:00
|
|
|
#include <stdio.h>
|
2005-12-08 10:23:34 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
2005-12-01 16:35:42 +00:00
|
|
|
#include <unistd.h>
|
2007-05-23 15:09:19 +00:00
|
|
|
#include <errno.h>
|
2005-12-08 10:23:34 +00:00
|
|
|
#include <getopt.h>
|
2005-12-08 14:22:52 +00:00
|
|
|
#include <sys/time.h>
|
2006-03-30 16:08:13 +00:00
|
|
|
#include <fcntl.h>
|
2007-11-29 09:18:04 +00:00
|
|
|
#include <time.h>
|
2007-06-15 15:24:20 +00:00
|
|
|
#include <limits.h>
|
2007-06-06 12:24:31 +00:00
|
|
|
#include <sys/stat.h>
|
2007-08-21 10:08:12 +00:00
|
|
|
#include <inttypes.h>
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
#include <signal.h>
|
2005-12-08 10:23:34 +00:00
|
|
|
|
2013-10-04 17:51:41 +00:00
|
|
|
#if WITH_READLINE
|
2010-03-09 18:22:22 +00:00
|
|
|
# include <readline/readline.h>
|
|
|
|
# include <readline/history.h>
|
2007-12-04 18:27:52 +00:00
|
|
|
#endif
|
2005-12-08 10:23:34 +00:00
|
|
|
|
Standardize use of header files, making internal.h primary.
* qemud/internal.h, qemud/qemud.h: Rename this file so it
doesn't conflict with src/internal.h.
* HACKING: Document how header files should be used.
* qemud/Makefile.am: Add src/ directory to includes.
* qemud/event.c, qemud/mdns.c, qemud/qemud.c, qemud/remote.c,
qemud/remote_protocol.c, qemud/remote_protocol.h,
qemud/remote_protocol.x, src/buf.c, src/libvirt.c,
src/nodeinfo.c, src/qemu_conf.c, src/qemu_driver.c,
src/stats_linux.c, src/storage_backend.c, src/storage_backend_fs.c,
src/storage_backend_iscsi.c, src/storage_backend_logical.c,
src/storage_conf.c, src/storage_driver.c, src/util.c,
src/util.h, src/virsh.c, src/virterror.c, src/xend_internal.c,
src/xml.c, tests/reconnect.c, tests/xmlrpctest.c,
tests/qparamtest.c: Standardize use of header files.
* docs/*, po/*: Rebuild docs.
2008-05-23 08:24:41 +00:00
|
|
|
#include "internal.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2013-04-17 10:19:19 +00:00
|
|
|
#include <libvirt/libvirt-qemu.h>
|
|
|
|
#include <libvirt/libvirt-lxc.h>
|
2011-07-19 18:32:58 +00:00
|
|
|
#include "virfile.h"
|
2012-12-13 15:49:48 +00:00
|
|
|
#include "virthread.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2011-12-20 08:35:03 +00:00
|
|
|
#include "conf/domain_conf.h"
|
2012-01-02 22:03:19 +00:00
|
|
|
#include "virtypedparam.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2016-04-12 22:29:52 +00:00
|
|
|
#include "virgettext.h"
|
2005-12-08 10:23:34 +00:00
|
|
|
|
2013-08-26 09:53:43 +00:00
|
|
|
#include "virsh-console.h"
|
2012-08-18 04:00:42 +00:00
|
|
|
#include "virsh-domain.h"
|
2012-08-20 13:46:38 +00:00
|
|
|
#include "virsh-domain-monitor.h"
|
2012-08-20 20:01:45 +00:00
|
|
|
#include "virsh-host.h"
|
2012-08-20 20:30:53 +00:00
|
|
|
#include "virsh-interface.h"
|
2012-08-20 21:43:25 +00:00
|
|
|
#include "virsh-network.h"
|
2012-08-20 22:23:10 +00:00
|
|
|
#include "virsh-nodedev.h"
|
2012-08-20 22:56:03 +00:00
|
|
|
#include "virsh-nwfilter.h"
|
2012-08-20 22:56:53 +00:00
|
|
|
#include "virsh-pool.h"
|
2012-08-20 23:14:37 +00:00
|
|
|
#include "virsh-secret.h"
|
2012-08-20 23:29:03 +00:00
|
|
|
#include "virsh-snapshot.h"
|
2012-08-20 23:41:24 +00:00
|
|
|
#include "virsh-volume.h"
|
2012-08-18 04:00:42 +00:00
|
|
|
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
/* Gnulib doesn't guarantee SA_SIGINFO support. */
|
|
|
|
#ifndef SA_SIGINFO
|
|
|
|
# define SA_SIGINFO 0
|
|
|
|
#endif
|
|
|
|
|
2005-12-08 10:23:34 +00:00
|
|
|
static char *progname;
|
|
|
|
|
2010-11-30 06:37:04 +00:00
|
|
|
static const vshCmdGrp cmdGroups[];
|
2015-06-15 16:53:58 +00:00
|
|
|
static const vshClientHooks hooks;
|
2009-02-09 14:24:06 +00:00
|
|
|
|
2010-03-05 09:59:52 +00:00
|
|
|
/*
|
|
|
|
* Detection of disconnections and automatic reconnection support
|
|
|
|
*/
|
2014-10-28 18:38:04 +00:00
|
|
|
static int disconnected; /* we may have been disconnected */
|
2010-03-05 09:59:52 +00:00
|
|
|
|
|
|
|
/*
|
2015-06-15 16:53:58 +00:00
|
|
|
* virshCatchDisconnect:
|
2010-03-05 09:59:52 +00:00
|
|
|
*
|
2012-08-02 17:15:16 +00:00
|
|
|
* We get here when the connection was closed. We can't do much in the
|
|
|
|
* handler, just save the fact it was raised.
|
2010-03-05 09:59:52 +00:00
|
|
|
*/
|
2010-03-12 10:39:24 +00:00
|
|
|
static void
|
2015-09-15 14:46:07 +00:00
|
|
|
virshCatchDisconnect(virConnectPtr conn,
|
2015-06-15 16:53:58 +00:00
|
|
|
int reason,
|
2015-09-15 14:46:07 +00:00
|
|
|
void *opaque)
|
2012-08-02 17:15:16 +00:00
|
|
|
{
|
2015-09-15 14:46:07 +00:00
|
|
|
if (reason != VIR_CONNECT_CLOSE_REASON_CLIENT) {
|
|
|
|
vshControl *ctl = opaque;
|
|
|
|
const char *str = "unknown reason";
|
|
|
|
virErrorPtr error;
|
|
|
|
char *uri;
|
|
|
|
|
|
|
|
error = virSaveLastError();
|
|
|
|
uri = virConnectGetURI(conn);
|
|
|
|
|
|
|
|
switch ((virConnectCloseReason) reason) {
|
|
|
|
case VIR_CONNECT_CLOSE_REASON_ERROR:
|
|
|
|
str = N_("Disconnected from %s due to I/O error");
|
|
|
|
break;
|
|
|
|
case VIR_CONNECT_CLOSE_REASON_EOF:
|
|
|
|
str = N_("Disconnected from %s due to end of file");
|
|
|
|
break;
|
|
|
|
case VIR_CONNECT_CLOSE_REASON_KEEPALIVE:
|
|
|
|
str = N_("Disconnected from %s due to keepalive timeout");
|
|
|
|
break;
|
2015-09-23 22:11:30 +00:00
|
|
|
/* coverity[dead_error_condition] */
|
2015-09-15 14:46:07 +00:00
|
|
|
case VIR_CONNECT_CLOSE_REASON_CLIENT:
|
|
|
|
case VIR_CONNECT_CLOSE_REASON_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vshError(ctl, _(str), NULLSTR(uri));
|
2016-03-03 16:31:47 +00:00
|
|
|
VIR_FREE(uri);
|
2015-09-15 14:46:07 +00:00
|
|
|
|
|
|
|
if (error) {
|
|
|
|
virSetError(error);
|
|
|
|
virFreeError(error);
|
|
|
|
}
|
2012-08-02 17:15:16 +00:00
|
|
|
disconnected++;
|
2015-12-17 22:23:16 +00:00
|
|
|
vshEventDone(ctl);
|
2015-09-15 14:46:07 +00:00
|
|
|
}
|
2010-03-05 09:59:52 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 16:20:11 +00:00
|
|
|
/* Main Function which should be used for connecting.
|
|
|
|
* This function properly handles keepalive settings. */
|
|
|
|
virConnectPtr
|
2015-06-15 16:53:58 +00:00
|
|
|
virshConnect(vshControl *ctl, const char *uri, bool readonly)
|
2014-03-06 16:20:11 +00:00
|
|
|
{
|
|
|
|
virConnectPtr c = NULL;
|
|
|
|
int interval = 5; /* Default */
|
|
|
|
int count = 6; /* Default */
|
|
|
|
bool keepalive_forced = false;
|
2016-02-09 19:08:42 +00:00
|
|
|
virPolkitAgentPtr pkagent = NULL;
|
|
|
|
int authfail = 0;
|
2017-05-11 13:17:09 +00:00
|
|
|
bool agentCreated = false;
|
2014-03-06 16:20:11 +00:00
|
|
|
|
|
|
|
if (ctl->keepalive_interval >= 0) {
|
|
|
|
interval = ctl->keepalive_interval;
|
|
|
|
keepalive_forced = true;
|
|
|
|
}
|
|
|
|
if (ctl->keepalive_count >= 0) {
|
|
|
|
count = ctl->keepalive_count;
|
|
|
|
keepalive_forced = true;
|
|
|
|
}
|
|
|
|
|
2016-02-09 19:08:42 +00:00
|
|
|
do {
|
|
|
|
virErrorPtr err;
|
|
|
|
|
|
|
|
if ((c = virConnectOpenAuth(uri, virConnectAuthPtrDefault,
|
|
|
|
readonly ? VIR_CONNECT_RO : 0)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (readonly)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
err = virGetLastError();
|
2017-05-11 13:17:09 +00:00
|
|
|
if (!agentCreated &&
|
|
|
|
err && err->domain == VIR_FROM_POLKIT &&
|
2016-02-09 19:08:42 +00:00
|
|
|
err->code == VIR_ERR_AUTH_UNAVAILABLE) {
|
2016-08-03 12:41:50 +00:00
|
|
|
if (!pkagent && !(pkagent = virPolkitAgentCreate()))
|
2016-02-09 19:08:42 +00:00
|
|
|
goto cleanup;
|
2017-05-11 13:17:09 +00:00
|
|
|
agentCreated = true;
|
2016-02-09 19:08:42 +00:00
|
|
|
} else if (err && err->domain == VIR_FROM_POLKIT &&
|
|
|
|
err->code == VIR_ERR_AUTH_FAILED) {
|
|
|
|
authfail++;
|
|
|
|
} else {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
virResetLastError();
|
|
|
|
/* Failure to authenticate 5 times should be enough.
|
|
|
|
* No sense prolonging the agony.
|
|
|
|
*/
|
|
|
|
} while (authfail < 5);
|
|
|
|
|
2014-03-06 16:20:11 +00:00
|
|
|
if (!c)
|
2016-02-09 19:08:42 +00:00
|
|
|
goto cleanup;
|
2014-03-06 16:20:11 +00:00
|
|
|
|
|
|
|
if (interval > 0 &&
|
|
|
|
virConnectSetKeepAlive(c, interval, count) != 0) {
|
|
|
|
if (keepalive_forced) {
|
|
|
|
vshError(ctl, "%s",
|
|
|
|
_("Cannot setup keepalive on connection "
|
|
|
|
"as requested, disconnecting"));
|
|
|
|
virConnectClose(c);
|
2016-02-09 19:08:42 +00:00
|
|
|
c = NULL;
|
|
|
|
goto cleanup;
|
2014-03-06 16:20:11 +00:00
|
|
|
}
|
|
|
|
vshDebug(ctl, VSH_ERR_INFO, "%s",
|
|
|
|
_("Failed to setup keepalive on connection\n"));
|
|
|
|
}
|
|
|
|
|
2016-02-09 19:08:42 +00:00
|
|
|
cleanup:
|
|
|
|
virPolkitAgentDestroy(pkagent);
|
2014-03-06 16:20:11 +00:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2010-03-05 09:59:52 +00:00
|
|
|
/*
|
2015-06-15 16:53:58 +00:00
|
|
|
* virshReconnect:
|
2010-03-05 09:59:52 +00:00
|
|
|
*
|
2010-03-12 10:39:24 +00:00
|
|
|
* Reconnect after a disconnect from libvirtd
|
2010-03-05 09:59:52 +00:00
|
|
|
*
|
|
|
|
*/
|
2016-07-29 06:09:22 +00:00
|
|
|
static int
|
2016-04-21 12:06:10 +00:00
|
|
|
virshReconnect(vshControl *ctl, const char *name, bool readonly, bool force)
|
virsh: optimize creation of default connection
Ramon de Carvalho Valle reported a problem with:
virsh connect qemu:///system
as a non-root user. The real root problem appears to be a regression
in libvirtd being auto-started on the default qemu:///session URI;
however, the symptom points to an independent flaw in virsh - we
shouldn't be wasting efforts on making a connection if we aren't going
to be using that connection. Fixing virsh avoids Ramon's issue, while
I work in the meantime to fix the real libvirtd regression.
This patch looks big, but that's because 'gcc -Wmissing-field-initializers'
gets triggered by './autobuild.sh --enable-compile-warnings=error', so I
had to add 0 initialization to everyone (rather than my preference of
just adding the non-zero flags to virshCmds and to cmdConnect).
Meanwhile, if you use 'virsh -c URI', the connection must succeed; this
patch _only_ optimizes the default connection to be deferred to a later
point where we know if a particular command to be run needs a connection.
* tools/virsh.c (VSH_CMD_FLAG_NOCONNECT): New flag.
(vshCmdDef): Add new flags field.
(vshCommandRun): Honor new flag.
(domManagementCmds, domMonitoringCmds, storagePoolCmds)
(storageVolCmds, networkCmds, nodedevCmds, ifaceCmds)
(nwfilterCmds, secretCmds, virshCmds, snapshotCmds)
(hostAndHypervisorCmds): Populate new field.
(vshReconnect): Don't warn on initial connection.
2011-03-14 20:30:24 +00:00
|
|
|
{
|
|
|
|
bool connected = false;
|
2015-06-15 16:53:58 +00:00
|
|
|
virshControlPtr priv = ctl->privData;
|
2016-04-21 12:06:10 +00:00
|
|
|
bool ro = name ? readonly : priv->readonly;
|
virsh: optimize creation of default connection
Ramon de Carvalho Valle reported a problem with:
virsh connect qemu:///system
as a non-root user. The real root problem appears to be a regression
in libvirtd being auto-started on the default qemu:///session URI;
however, the symptom points to an independent flaw in virsh - we
shouldn't be wasting efforts on making a connection if we aren't going
to be using that connection. Fixing virsh avoids Ramon's issue, while
I work in the meantime to fix the real libvirtd regression.
This patch looks big, but that's because 'gcc -Wmissing-field-initializers'
gets triggered by './autobuild.sh --enable-compile-warnings=error', so I
had to add 0 initialization to everyone (rather than my preference of
just adding the non-zero flags to virshCmds and to cmdConnect).
Meanwhile, if you use 'virsh -c URI', the connection must succeed; this
patch _only_ optimizes the default connection to be deferred to a later
point where we know if a particular command to be run needs a connection.
* tools/virsh.c (VSH_CMD_FLAG_NOCONNECT): New flag.
(vshCmdDef): Add new flags field.
(vshCommandRun): Honor new flag.
(domManagementCmds, domMonitoringCmds, storagePoolCmds)
(storageVolCmds, networkCmds, nodedevCmds, ifaceCmds)
(nwfilterCmds, secretCmds, virshCmds, snapshotCmds)
(hostAndHypervisorCmds): Populate new field.
(vshReconnect): Don't warn on initial connection.
2011-03-14 20:30:24 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
if (priv->conn) {
|
2013-03-26 09:54:55 +00:00
|
|
|
int ret;
|
virsh: optimize creation of default connection
Ramon de Carvalho Valle reported a problem with:
virsh connect qemu:///system
as a non-root user. The real root problem appears to be a regression
in libvirtd being auto-started on the default qemu:///session URI;
however, the symptom points to an independent flaw in virsh - we
shouldn't be wasting efforts on making a connection if we aren't going
to be using that connection. Fixing virsh avoids Ramon's issue, while
I work in the meantime to fix the real libvirtd regression.
This patch looks big, but that's because 'gcc -Wmissing-field-initializers'
gets triggered by './autobuild.sh --enable-compile-warnings=error', so I
had to add 0 initialization to everyone (rather than my preference of
just adding the non-zero flags to virshCmds and to cmdConnect).
Meanwhile, if you use 'virsh -c URI', the connection must succeed; this
patch _only_ optimizes the default connection to be deferred to a later
point where we know if a particular command to be run needs a connection.
* tools/virsh.c (VSH_CMD_FLAG_NOCONNECT): New flag.
(vshCmdDef): Add new flags field.
(vshCommandRun): Honor new flag.
(domManagementCmds, domMonitoringCmds, storagePoolCmds)
(storageVolCmds, networkCmds, nodedevCmds, ifaceCmds)
(nwfilterCmds, secretCmds, virshCmds, snapshotCmds)
(hostAndHypervisorCmds): Populate new field.
(vshReconnect): Don't warn on initial connection.
2011-03-14 20:30:24 +00:00
|
|
|
connected = true;
|
2013-03-26 09:54:55 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect);
|
|
|
|
ret = virConnectClose(priv->conn);
|
2013-03-26 09:54:55 +00:00
|
|
|
if (ret < 0)
|
|
|
|
vshError(ctl, "%s", _("Failed to disconnect from the hypervisor"));
|
|
|
|
else if (ret > 0)
|
|
|
|
vshError(ctl, "%s", _("One or more references were leaked after "
|
|
|
|
"disconnect from the hypervisor"));
|
virsh: optimize creation of default connection
Ramon de Carvalho Valle reported a problem with:
virsh connect qemu:///system
as a non-root user. The real root problem appears to be a regression
in libvirtd being auto-started on the default qemu:///session URI;
however, the symptom points to an independent flaw in virsh - we
shouldn't be wasting efforts on making a connection if we aren't going
to be using that connection. Fixing virsh avoids Ramon's issue, while
I work in the meantime to fix the real libvirtd regression.
This patch looks big, but that's because 'gcc -Wmissing-field-initializers'
gets triggered by './autobuild.sh --enable-compile-warnings=error', so I
had to add 0 initialization to everyone (rather than my preference of
just adding the non-zero flags to virshCmds and to cmdConnect).
Meanwhile, if you use 'virsh -c URI', the connection must succeed; this
patch _only_ optimizes the default connection to be deferred to a later
point where we know if a particular command to be run needs a connection.
* tools/virsh.c (VSH_CMD_FLAG_NOCONNECT): New flag.
(vshCmdDef): Add new flags field.
(vshCommandRun): Honor new flag.
(domManagementCmds, domMonitoringCmds, storagePoolCmds)
(storageVolCmds, networkCmds, nodedevCmds, ifaceCmds)
(nwfilterCmds, secretCmds, virshCmds, snapshotCmds)
(hostAndHypervisorCmds): Populate new field.
(vshReconnect): Don't warn on initial connection.
2011-03-14 20:30:24 +00:00
|
|
|
}
|
2010-03-05 09:59:52 +00:00
|
|
|
|
2016-04-21 12:06:10 +00:00
|
|
|
priv->conn = virshConnect(ctl, name ? name : ctl->connname, ro);
|
2014-03-06 16:20:11 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
if (!priv->conn) {
|
2012-12-13 13:08:00 +00:00
|
|
|
if (disconnected)
|
|
|
|
vshError(ctl, "%s", _("Failed to reconnect to the hypervisor"));
|
|
|
|
else
|
|
|
|
vshError(ctl, "%s", _("failed to connect to the hypervisor"));
|
2016-07-29 06:09:22 +00:00
|
|
|
return -1;
|
2012-08-02 17:15:16 +00:00
|
|
|
} else {
|
2016-04-21 12:06:10 +00:00
|
|
|
if (name) {
|
|
|
|
VIR_FREE(ctl->connname);
|
|
|
|
ctl->connname = vshStrdup(ctl, name);
|
|
|
|
priv->readonly = readonly;
|
|
|
|
}
|
2015-06-15 16:53:58 +00:00
|
|
|
if (virConnectRegisterCloseCallback(priv->conn, virshCatchDisconnect,
|
2015-12-17 22:23:01 +00:00
|
|
|
ctl, NULL) < 0)
|
2012-08-02 17:15:16 +00:00
|
|
|
vshError(ctl, "%s", _("Unable to register disconnect callback"));
|
2016-04-21 12:06:10 +00:00
|
|
|
if (connected && !force)
|
2012-08-02 17:15:16 +00:00
|
|
|
vshError(ctl, "%s", _("Reconnected to the hypervisor"));
|
|
|
|
}
|
2010-03-05 09:59:52 +00:00
|
|
|
disconnected = 0;
|
2015-06-15 16:53:58 +00:00
|
|
|
priv->useGetInfo = false;
|
|
|
|
priv->useSnapshotOld = false;
|
|
|
|
priv->blockJobNoBytes = false;
|
2016-07-29 06:09:22 +00:00
|
|
|
return 0;
|
2010-03-05 09:59:52 +00:00
|
|
|
}
|
2007-02-14 15:44:58 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
/* ---------------
|
|
|
|
* Command Connect
|
|
|
|
* ---------------
|
2013-03-27 13:22:47 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
static const vshCmdOptDef opts_connect[] = {
|
|
|
|
{.name = "name",
|
2014-12-11 02:46:15 +00:00
|
|
|
.type = VSH_OT_STRING,
|
2013-03-27 13:22:47 +00:00
|
|
|
.flags = VSH_OFLAG_EMPTY_OK,
|
|
|
|
.help = N_("hypervisor connection URI")
|
|
|
|
},
|
|
|
|
{.name = "readonly",
|
|
|
|
.type = VSH_OT_BOOL,
|
|
|
|
.help = N_("read-only connection")
|
|
|
|
},
|
|
|
|
{.name = NULL}
|
|
|
|
};
|
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
static const vshCmdInfo info_connect[] = {
|
2013-02-07 15:25:10 +00:00
|
|
|
{.name = "help",
|
2015-06-15 16:53:58 +00:00
|
|
|
.data = N_("(re)connect to hypervisor")
|
2013-02-07 15:25:10 +00:00
|
|
|
},
|
|
|
|
{.name = "desc",
|
2015-06-15 16:53:58 +00:00
|
|
|
.data = N_("Connect to local hypervisor. This is built-in "
|
|
|
|
"command after shell start up.")
|
2013-01-14 11:26:23 +00:00
|
|
|
},
|
|
|
|
{.name = NULL}
|
2009-07-16 14:40:08 +00:00
|
|
|
};
|
|
|
|
|
2011-04-18 22:37:42 +00:00
|
|
|
static bool
|
2015-06-15 16:53:58 +00:00
|
|
|
cmdConnect(vshControl *ctl, const vshCmd *cmd)
|
2009-07-16 14:40:08 +00:00
|
|
|
{
|
2015-06-15 16:53:58 +00:00
|
|
|
bool ro = vshCommandOptBool(cmd, "readonly");
|
|
|
|
const char *name = NULL;
|
2016-04-20 17:56:27 +00:00
|
|
|
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0)
|
|
|
|
return false;
|
|
|
|
|
2016-07-29 06:09:22 +00:00
|
|
|
if (virshReconnect(ctl, name, ro, true) < 0)
|
|
|
|
return false;
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
return true;
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
/* ---------------
|
|
|
|
* Utils for work with runtime commands data
|
|
|
|
* ---------------
|
|
|
|
*/
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
static bool
|
|
|
|
virshConnectionUsability(vshControl *ctl, virConnectPtr conn)
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
{
|
2015-06-15 16:53:58 +00:00
|
|
|
if (!conn ||
|
|
|
|
virConnectIsAlive(conn) == 0) {
|
|
|
|
vshError(ctl, "%s", _("no valid connection"));
|
|
|
|
return false;
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
/* The connection is considered dead only if
|
|
|
|
* virConnectIsAlive() successfuly says so.
|
|
|
|
*/
|
|
|
|
vshResetLibvirtError();
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
return true;
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
static void *
|
|
|
|
virshConnectionHandler(vshControl *ctl)
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
{
|
2015-06-15 16:53:58 +00:00
|
|
|
virshControlPtr priv = ctl->privData;
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
|
2016-07-29 06:09:22 +00:00
|
|
|
if ((!priv->conn || disconnected) &&
|
|
|
|
virshReconnect(ctl, NULL, false, false) < 0)
|
|
|
|
return NULL;
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
if (virshConnectionUsability(ctl, priv->conn))
|
|
|
|
return priv->conn;
|
2016-07-29 06:09:22 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
return NULL;
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
}
|
|
|
|
|
2016-06-17 17:28:12 +00:00
|
|
|
|
2013-08-21 09:02:42 +00:00
|
|
|
/*
|
|
|
|
* Initialize connection.
|
|
|
|
*/
|
|
|
|
static bool
|
2015-06-15 16:53:58 +00:00
|
|
|
virshInit(vshControl *ctl)
|
2013-08-21 09:02:42 +00:00
|
|
|
{
|
2015-06-15 16:53:58 +00:00
|
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
|
2013-08-27 11:19:24 +00:00
|
|
|
/* Since we have the commandline arguments parsed, we need to
|
2015-09-03 14:59:01 +00:00
|
|
|
* reload our initial settings to make debugging and readline
|
|
|
|
* work properly */
|
|
|
|
vshInitReload(ctl);
|
2013-08-27 11:19:24 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
if (priv->conn)
|
2013-08-21 09:02:42 +00:00
|
|
|
return false;
|
2007-06-06 12:24:31 +00:00
|
|
|
|
2006-02-27 21:34:28 +00:00
|
|
|
/* set up the library error handler */
|
2015-06-15 16:53:58 +00:00
|
|
|
virSetErrorFunc(NULL, vshErrorHandler);
|
2006-03-15 12:13:25 +00:00
|
|
|
|
2017-04-19 10:42:00 +00:00
|
|
|
if (virEventRegisterDefaultImpl() < 0) {
|
|
|
|
vshReportError(ctl);
|
2011-04-18 22:37:42 +00:00
|
|
|
return false;
|
2017-04-19 10:42:00 +00:00
|
|
|
}
|
2010-07-27 09:40:30 +00:00
|
|
|
|
2017-04-19 10:42:00 +00:00
|
|
|
if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0) {
|
|
|
|
vshReportError(ctl);
|
2011-10-11 13:05:52 +00:00
|
|
|
return false;
|
2017-04-19 10:42:00 +00:00
|
|
|
}
|
2011-10-11 13:05:52 +00:00
|
|
|
ctl->eventLoopStarted = true;
|
|
|
|
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
if ((ctl->eventTimerId = virEventAddTimeout(-1, vshEventTimeout, ctl,
|
2017-04-19 10:42:00 +00:00
|
|
|
NULL)) < 0) {
|
|
|
|
vshReportError(ctl);
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
return false;
|
2017-04-19 10:42:00 +00:00
|
|
|
}
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
|
2015-12-10 12:46:45 +00:00
|
|
|
if (ctl->connname) {
|
virsh: optimize creation of default connection
Ramon de Carvalho Valle reported a problem with:
virsh connect qemu:///system
as a non-root user. The real root problem appears to be a regression
in libvirtd being auto-started on the default qemu:///session URI;
however, the symptom points to an independent flaw in virsh - we
shouldn't be wasting efforts on making a connection if we aren't going
to be using that connection. Fixing virsh avoids Ramon's issue, while
I work in the meantime to fix the real libvirtd regression.
This patch looks big, but that's because 'gcc -Wmissing-field-initializers'
gets triggered by './autobuild.sh --enable-compile-warnings=error', so I
had to add 0 initialization to everyone (rather than my preference of
just adding the non-zero flags to virshCmds and to cmdConnect).
Meanwhile, if you use 'virsh -c URI', the connection must succeed; this
patch _only_ optimizes the default connection to be deferred to a later
point where we know if a particular command to be run needs a connection.
* tools/virsh.c (VSH_CMD_FLAG_NOCONNECT): New flag.
(vshCmdDef): Add new flags field.
(vshCommandRun): Honor new flag.
(domManagementCmds, domMonitoringCmds, storagePoolCmds)
(storageVolCmds, networkCmds, nodedevCmds, ifaceCmds)
(nwfilterCmds, secretCmds, virshCmds, snapshotCmds)
(hostAndHypervisorCmds): Populate new field.
(vshReconnect): Don't warn on initial connection.
2011-03-14 20:30:24 +00:00
|
|
|
/* Connecting to a named connection must succeed, but we delay
|
|
|
|
* connecting to the default connection until we need it
|
|
|
|
* (since the first command might be 'connect' which allows a
|
|
|
|
* non-default connection, or might be 'help' which needs no
|
|
|
|
* connection).
|
|
|
|
*/
|
2016-07-29 06:09:22 +00:00
|
|
|
if (virshReconnect(ctl, NULL, false, false) < 0) {
|
virsh: use common namespacing
Convert the exported items in virsh.h to use a common 'vsh' prefix.
* tools/virsh.h (VIRSH_MAX_XML_FILE): Rename...
(VSH_MAX_XML_FILE): ...and parenthesize.
(DIFF_MSEC, CTRL_CLOSE_BRACKET): Delete.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Remove prototype.
(editWriteToTempFile, editFile, editReadBackFile, prettyCapacity)
(virshReportError): Rename...
(vshEditWriteToTempFile, vshEditFile, vshEditReadBackFile)
(vshPrettyCapacity, vshReportError): ...into vsh namespace.
(jobWatchTimeoutFunc): Move to virsh-domain.c.
* tools/virsh.c (vshCommandRun): Inline former DIFF_MSEC.
(main): Inline former CTRL_CLOSE_BRACKET.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Make static.
(prettyCapacity, virshReportError, editWriteToTempFile, editFile):
Fix naming, and adjust usage.
(vshAskReedit, vshCommandRun, vshEventLoop, vshInit): Adjust
usage.
* tools/virsh-domain.c (cmdAttachDevice, cmdCPUCompare)
(cmdCPUBaseline, cmdCreate, cmdDefine, cmdDetachDevice)
(cmdUpdateDevice, cmdDesc, cmdUndefine, cmdStart, cmdVcpucount)
(cmdAttachDevice, cmdDomjobinfo): Likewise.
* tools/virsh-edit.c (do): Likewise.
* tools/virsh-interface.c (cmdInterfaceDefine): Likewise.
* tools/virsh-network.c (cmdNetworkCreate, cmdNetworkDefine):
Likewise.
* tools/virsh-nodedev.c (cmdNodeDeviceCreate): Likewise.
* tools/virsh-nwfilter.c (cmdNWFilterDefine): Likewise.
* tools/virsh-pool.c (cmdPoolCreate, cmdPoolDefine)
(cmdPoolDiscoverSources, cmdPoolList): Likewise.
* tools/virsh-secret.c (cmdSecretDefine): Likewise.
* tools/virsh-snapshot.c (cmdSnapshotCreate, vshSnapshotCreate)
(vshLookupSnapshot, cmdSnapshotEdit, cmdSnapshotCurrent)
(vshGetSnapshotParent): Likewise.
* tools/virsh-volume.c (cmdVolCreate, cmdVolCreateFrom)
(cmdVolInfo, cmdVolList): Likewise.
2012-08-19 04:10:17 +00:00
|
|
|
vshReportError(ctl);
|
virsh: optimize creation of default connection
Ramon de Carvalho Valle reported a problem with:
virsh connect qemu:///system
as a non-root user. The real root problem appears to be a regression
in libvirtd being auto-started on the default qemu:///session URI;
however, the symptom points to an independent flaw in virsh - we
shouldn't be wasting efforts on making a connection if we aren't going
to be using that connection. Fixing virsh avoids Ramon's issue, while
I work in the meantime to fix the real libvirtd regression.
This patch looks big, but that's because 'gcc -Wmissing-field-initializers'
gets triggered by './autobuild.sh --enable-compile-warnings=error', so I
had to add 0 initialization to everyone (rather than my preference of
just adding the non-zero flags to virshCmds and to cmdConnect).
Meanwhile, if you use 'virsh -c URI', the connection must succeed; this
patch _only_ optimizes the default connection to be deferred to a later
point where we know if a particular command to be run needs a connection.
* tools/virsh.c (VSH_CMD_FLAG_NOCONNECT): New flag.
(vshCmdDef): Add new flags field.
(vshCommandRun): Honor new flag.
(domManagementCmds, domMonitoringCmds, storagePoolCmds)
(storageVolCmds, networkCmds, nodedevCmds, ifaceCmds)
(nwfilterCmds, secretCmds, virshCmds, snapshotCmds)
(hostAndHypervisorCmds): Populate new field.
(vshReconnect): Don't warn on initial connection.
2011-03-14 20:30:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
2007-12-05 16:24:22 +00:00
|
|
|
}
|
2005-12-08 10:23:34 +00:00
|
|
|
|
2011-04-18 22:37:42 +00:00
|
|
|
return true;
|
2005-12-08 10:23:34 +00:00
|
|
|
}
|
|
|
|
|
2011-11-30 19:42:20 +00:00
|
|
|
static void
|
2015-06-15 16:53:58 +00:00
|
|
|
virshDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED)
|
2011-11-30 19:42:20 +00:00
|
|
|
{
|
|
|
|
/* nothing to be done here */
|
|
|
|
}
|
|
|
|
|
2005-12-08 10:23:34 +00:00
|
|
|
/*
|
2008-02-04 14:58:47 +00:00
|
|
|
* Deinitialize virsh
|
2005-12-08 10:23:34 +00:00
|
|
|
*/
|
virsh: use common namespacing
Convert the exported items in virsh.h to use a common 'vsh' prefix.
* tools/virsh.h (VIRSH_MAX_XML_FILE): Rename...
(VSH_MAX_XML_FILE): ...and parenthesize.
(DIFF_MSEC, CTRL_CLOSE_BRACKET): Delete.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Remove prototype.
(editWriteToTempFile, editFile, editReadBackFile, prettyCapacity)
(virshReportError): Rename...
(vshEditWriteToTempFile, vshEditFile, vshEditReadBackFile)
(vshPrettyCapacity, vshReportError): ...into vsh namespace.
(jobWatchTimeoutFunc): Move to virsh-domain.c.
* tools/virsh.c (vshCommandRun): Inline former DIFF_MSEC.
(main): Inline former CTRL_CLOSE_BRACKET.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Make static.
(prettyCapacity, virshReportError, editWriteToTempFile, editFile):
Fix naming, and adjust usage.
(vshAskReedit, vshCommandRun, vshEventLoop, vshInit): Adjust
usage.
* tools/virsh-domain.c (cmdAttachDevice, cmdCPUCompare)
(cmdCPUBaseline, cmdCreate, cmdDefine, cmdDetachDevice)
(cmdUpdateDevice, cmdDesc, cmdUndefine, cmdStart, cmdVcpucount)
(cmdAttachDevice, cmdDomjobinfo): Likewise.
* tools/virsh-edit.c (do): Likewise.
* tools/virsh-interface.c (cmdInterfaceDefine): Likewise.
* tools/virsh-network.c (cmdNetworkCreate, cmdNetworkDefine):
Likewise.
* tools/virsh-nodedev.c (cmdNodeDeviceCreate): Likewise.
* tools/virsh-nwfilter.c (cmdNWFilterDefine): Likewise.
* tools/virsh-pool.c (cmdPoolCreate, cmdPoolDefine)
(cmdPoolDiscoverSources, cmdPoolList): Likewise.
* tools/virsh-secret.c (cmdSecretDefine): Likewise.
* tools/virsh-snapshot.c (cmdSnapshotCreate, vshSnapshotCreate)
(vshLookupSnapshot, cmdSnapshotEdit, cmdSnapshotCurrent)
(vshGetSnapshotParent): Likewise.
* tools/virsh-volume.c (cmdVolCreate, cmdVolCreateFrom)
(cmdVolInfo, cmdVolList): Likewise.
2012-08-19 04:10:17 +00:00
|
|
|
static bool
|
2015-06-15 16:53:58 +00:00
|
|
|
virshDeinit(vshControl *ctl)
|
2006-03-15 12:13:25 +00:00
|
|
|
{
|
2015-06-15 16:53:58 +00:00
|
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
|
|
|
|
vshDeinit(ctl);
|
2015-12-10 12:46:45 +00:00
|
|
|
VIR_FREE(ctl->connname);
|
2015-06-15 16:53:58 +00:00
|
|
|
if (priv->conn) {
|
2010-11-11 15:15:46 +00:00
|
|
|
int ret;
|
2015-06-15 16:53:58 +00:00
|
|
|
virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect);
|
|
|
|
ret = virConnectClose(priv->conn);
|
2013-03-26 09:54:55 +00:00
|
|
|
if (ret < 0)
|
|
|
|
vshError(ctl, "%s", _("Failed to disconnect from the hypervisor"));
|
|
|
|
else if (ret > 0)
|
|
|
|
vshError(ctl, "%s", _("One or more references were leaked after "
|
|
|
|
"disconnect from the hypervisor"));
|
2005-12-08 10:23:34 +00:00
|
|
|
}
|
2007-12-01 15:45:25 +00:00
|
|
|
virResetLastError();
|
|
|
|
|
2011-10-11 13:05:52 +00:00
|
|
|
if (ctl->eventLoopStarted) {
|
2011-11-30 19:42:20 +00:00
|
|
|
int timer;
|
|
|
|
|
|
|
|
virMutexLock(&ctl->lock);
|
|
|
|
ctl->quit = true;
|
2011-10-11 13:05:52 +00:00
|
|
|
/* HACK: Add a dummy timeout to break event loop */
|
2015-06-15 16:53:58 +00:00
|
|
|
timer = virEventAddTimeout(0, virshDeinitTimer, NULL, NULL);
|
2011-11-30 19:42:20 +00:00
|
|
|
virMutexUnlock(&ctl->lock);
|
|
|
|
|
|
|
|
virThreadJoin(&ctl->eventLoop);
|
|
|
|
|
2011-10-11 13:05:52 +00:00
|
|
|
if (timer != -1)
|
|
|
|
virEventRemoveTimeout(timer);
|
|
|
|
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
if (ctl->eventTimerId != -1)
|
|
|
|
virEventRemoveTimeout(ctl->eventTimerId);
|
|
|
|
|
2011-10-11 13:05:52 +00:00
|
|
|
ctl->eventLoopStarted = false;
|
|
|
|
}
|
|
|
|
|
2011-11-30 19:42:20 +00:00
|
|
|
virMutexDestroy(&ctl->lock);
|
|
|
|
|
2011-04-18 22:37:42 +00:00
|
|
|
return true;
|
2005-12-08 10:23:34 +00:00
|
|
|
}
|
2006-03-15 12:13:25 +00:00
|
|
|
|
2005-12-08 10:23:34 +00:00
|
|
|
/*
|
|
|
|
* Print usage
|
|
|
|
*/
|
virsh: use common namespacing
Convert the exported items in virsh.h to use a common 'vsh' prefix.
* tools/virsh.h (VIRSH_MAX_XML_FILE): Rename...
(VSH_MAX_XML_FILE): ...and parenthesize.
(DIFF_MSEC, CTRL_CLOSE_BRACKET): Delete.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Remove prototype.
(editWriteToTempFile, editFile, editReadBackFile, prettyCapacity)
(virshReportError): Rename...
(vshEditWriteToTempFile, vshEditFile, vshEditReadBackFile)
(vshPrettyCapacity, vshReportError): ...into vsh namespace.
(jobWatchTimeoutFunc): Move to virsh-domain.c.
* tools/virsh.c (vshCommandRun): Inline former DIFF_MSEC.
(main): Inline former CTRL_CLOSE_BRACKET.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Make static.
(prettyCapacity, virshReportError, editWriteToTempFile, editFile):
Fix naming, and adjust usage.
(vshAskReedit, vshCommandRun, vshEventLoop, vshInit): Adjust
usage.
* tools/virsh-domain.c (cmdAttachDevice, cmdCPUCompare)
(cmdCPUBaseline, cmdCreate, cmdDefine, cmdDetachDevice)
(cmdUpdateDevice, cmdDesc, cmdUndefine, cmdStart, cmdVcpucount)
(cmdAttachDevice, cmdDomjobinfo): Likewise.
* tools/virsh-edit.c (do): Likewise.
* tools/virsh-interface.c (cmdInterfaceDefine): Likewise.
* tools/virsh-network.c (cmdNetworkCreate, cmdNetworkDefine):
Likewise.
* tools/virsh-nodedev.c (cmdNodeDeviceCreate): Likewise.
* tools/virsh-nwfilter.c (cmdNWFilterDefine): Likewise.
* tools/virsh-pool.c (cmdPoolCreate, cmdPoolDefine)
(cmdPoolDiscoverSources, cmdPoolList): Likewise.
* tools/virsh-secret.c (cmdSecretDefine): Likewise.
* tools/virsh-snapshot.c (cmdSnapshotCreate, vshSnapshotCreate)
(vshLookupSnapshot, cmdSnapshotEdit, cmdSnapshotCurrent)
(vshGetSnapshotParent): Likewise.
* tools/virsh-volume.c (cmdVolCreate, cmdVolCreateFrom)
(cmdVolInfo, cmdVolList): Likewise.
2012-08-19 04:10:17 +00:00
|
|
|
static void
|
2015-06-15 16:53:58 +00:00
|
|
|
virshUsage(void)
|
2006-03-15 12:13:25 +00:00
|
|
|
{
|
2010-11-30 06:37:04 +00:00
|
|
|
const vshCmdGrp *grp;
|
2008-08-01 12:19:56 +00:00
|
|
|
const vshCmdDef *cmd;
|
2010-11-30 06:37:04 +00:00
|
|
|
|
2010-10-12 07:14:01 +00:00
|
|
|
fprintf(stdout, _("\n%s [options]... [<command_string>]"
|
|
|
|
"\n%s [options]... <command> [args...]\n\n"
|
2008-12-08 13:14:48 +00:00
|
|
|
" options:\n"
|
2011-11-22 16:08:05 +00:00
|
|
|
" -c | --connect=URI hypervisor connection URI\n"
|
|
|
|
" -d | --debug=NUM debug level [0-4]\n"
|
2014-03-06 15:53:53 +00:00
|
|
|
" -e | --escape <char> set escape sequence for console\n"
|
2008-12-08 13:14:48 +00:00
|
|
|
" -h | --help this help\n"
|
2014-03-06 16:20:11 +00:00
|
|
|
" -k | --keepalive-interval=NUM\n"
|
|
|
|
" keepalive interval in seconds, 0 for disable\n"
|
|
|
|
" -K | --keepalive-count=NUM\n"
|
|
|
|
" number of possible missed keepalive messages\n"
|
2014-03-06 15:53:53 +00:00
|
|
|
" -l | --log=FILE output logging to file\n"
|
2008-12-08 13:14:48 +00:00
|
|
|
" -q | --quiet quiet mode\n"
|
2014-03-06 15:53:53 +00:00
|
|
|
" -r | --readonly connect readonly\n"
|
2008-12-08 13:14:48 +00:00
|
|
|
" -t | --timing print timing information\n"
|
2011-11-22 16:08:05 +00:00
|
|
|
" -v short version\n"
|
|
|
|
" -V long version\n"
|
|
|
|
" --version[=TYPE] version, TYPE is short or long (default short)\n"
|
2015-06-15 16:53:58 +00:00
|
|
|
" commands (non interactive mode):\n\n"), progname,
|
|
|
|
progname);
|
2008-12-08 13:14:48 +00:00
|
|
|
|
2010-11-30 06:37:04 +00:00
|
|
|
for (grp = cmdGroups; grp->name; grp++) {
|
2012-03-16 19:23:00 +00:00
|
|
|
fprintf(stdout, _(" %s (help keyword '%s')\n"),
|
|
|
|
grp->name, grp->keyword);
|
|
|
|
for (cmd = grp->commands; cmd->name; cmd++) {
|
|
|
|
if (cmd->flags & VSH_CMD_FLAG_ALIAS)
|
|
|
|
continue;
|
2010-11-30 06:37:04 +00:00
|
|
|
fprintf(stdout,
|
2012-03-16 19:23:00 +00:00
|
|
|
" %-30s %s\n", cmd->name,
|
|
|
|
_(vshCmddefGetInfo(cmd, "help")));
|
|
|
|
}
|
2010-11-30 06:37:04 +00:00
|
|
|
fprintf(stdout, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stdout, "%s",
|
|
|
|
_("\n (specify help <group> for details about the commands in the group)\n"));
|
2008-12-08 13:14:48 +00:00
|
|
|
fprintf(stdout, "%s",
|
|
|
|
_("\n (specify help <command> for details about the command)\n\n"));
|
|
|
|
return;
|
2005-12-08 10:23:34 +00:00
|
|
|
}
|
|
|
|
|
2010-11-08 14:03:32 +00:00
|
|
|
/*
|
|
|
|
* Show version and options compiled in
|
|
|
|
*/
|
|
|
|
static void
|
2015-06-15 16:53:58 +00:00
|
|
|
virshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
|
2010-11-08 14:03:32 +00:00
|
|
|
{
|
|
|
|
/* FIXME - list a copyright blurb, as in GNU programs? */
|
|
|
|
vshPrint(ctl, _("Virsh command line tool of libvirt %s\n"), VERSION);
|
|
|
|
vshPrint(ctl, _("See web site at %s\n\n"), "http://libvirt.org/");
|
|
|
|
|
2010-11-09 22:37:05 +00:00
|
|
|
vshPrint(ctl, "%s", _("Compiled with support for:\n"));
|
|
|
|
vshPrint(ctl, "%s", _(" Hypervisors:"));
|
2010-11-08 14:03:32 +00:00
|
|
|
#ifdef WITH_QEMU
|
2012-09-09 22:07:28 +00:00
|
|
|
vshPrint(ctl, " QEMU/KVM");
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
2012-06-26 18:31:31 +00:00
|
|
|
#ifdef WITH_LXC
|
|
|
|
vshPrint(ctl, " LXC");
|
|
|
|
#endif
|
2010-11-08 14:03:32 +00:00
|
|
|
#ifdef WITH_UML
|
|
|
|
vshPrint(ctl, " UML");
|
|
|
|
#endif
|
2012-06-26 18:31:31 +00:00
|
|
|
#ifdef WITH_XEN
|
|
|
|
vshPrint(ctl, " Xen");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_LIBXL
|
|
|
|
vshPrint(ctl, " LibXL");
|
|
|
|
#endif
|
2010-11-08 14:03:32 +00:00
|
|
|
#ifdef WITH_OPENVZ
|
|
|
|
vshPrint(ctl, " OpenVZ");
|
|
|
|
#endif
|
2016-03-28 13:20:40 +00:00
|
|
|
#ifdef WITH_VZ
|
|
|
|
vshPrint(ctl, " Virtuozzo");
|
|
|
|
#endif
|
2012-06-26 18:31:31 +00:00
|
|
|
#ifdef WITH_VMWARE
|
2016-02-15 14:34:24 +00:00
|
|
|
vshPrint(ctl, " VMware");
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
2012-06-26 18:31:31 +00:00
|
|
|
#ifdef WITH_PHYP
|
|
|
|
vshPrint(ctl, " PHYP");
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
2012-06-26 18:31:31 +00:00
|
|
|
#ifdef WITH_VBOX
|
|
|
|
vshPrint(ctl, " VirtualBox");
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
|
|
|
#ifdef WITH_ESX
|
|
|
|
vshPrint(ctl, " ESX");
|
|
|
|
#endif
|
2012-06-26 18:31:31 +00:00
|
|
|
#ifdef WITH_HYPERV
|
|
|
|
vshPrint(ctl, " Hyper-V");
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
2012-06-26 18:31:31 +00:00
|
|
|
#ifdef WITH_XENAPI
|
|
|
|
vshPrint(ctl, " XenAPI");
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
2014-06-10 18:25:10 +00:00
|
|
|
#ifdef WITH_BHYVE
|
|
|
|
vshPrint(ctl, " Bhyve");
|
|
|
|
#endif
|
2010-11-08 14:03:32 +00:00
|
|
|
#ifdef WITH_TEST
|
|
|
|
vshPrint(ctl, " Test");
|
|
|
|
#endif
|
|
|
|
vshPrint(ctl, "\n");
|
|
|
|
|
2010-11-09 22:37:05 +00:00
|
|
|
vshPrint(ctl, "%s", _(" Networking:"));
|
2010-11-08 14:03:32 +00:00
|
|
|
#ifdef WITH_REMOTE
|
|
|
|
vshPrint(ctl, " Remote");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_NETWORK
|
|
|
|
vshPrint(ctl, " Network");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_BRIDGE
|
|
|
|
vshPrint(ctl, " Bridging");
|
|
|
|
#endif
|
2012-09-18 01:27:06 +00:00
|
|
|
#if defined(WITH_INTERFACE)
|
2012-06-26 18:31:31 +00:00
|
|
|
vshPrint(ctl, " Interface");
|
2012-09-18 01:27:06 +00:00
|
|
|
# if defined(WITH_NETCF)
|
|
|
|
vshPrint(ctl, " netcf");
|
2012-09-20 14:24:47 +00:00
|
|
|
# elif defined(WITH_UDEV)
|
2012-10-06 19:20:25 +00:00
|
|
|
vshPrint(ctl, " udev");
|
2012-09-18 01:27:06 +00:00
|
|
|
# endif
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
|
|
|
#ifdef WITH_NWFILTER
|
|
|
|
vshPrint(ctl, " Nwfilter");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_VIRTUALPORT
|
|
|
|
vshPrint(ctl, " VirtualPort");
|
|
|
|
#endif
|
|
|
|
vshPrint(ctl, "\n");
|
|
|
|
|
2010-11-09 22:37:05 +00:00
|
|
|
vshPrint(ctl, "%s", _(" Storage:"));
|
2010-11-08 14:03:32 +00:00
|
|
|
#ifdef WITH_STORAGE_DIR
|
|
|
|
vshPrint(ctl, " Dir");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_DISK
|
|
|
|
vshPrint(ctl, " Disk");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_FS
|
|
|
|
vshPrint(ctl, " Filesystem");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_SCSI
|
|
|
|
vshPrint(ctl, " SCSI");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_MPATH
|
|
|
|
vshPrint(ctl, " Multipath");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_ISCSI
|
|
|
|
vshPrint(ctl, " iSCSI");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_LVM
|
|
|
|
vshPrint(ctl, " LVM");
|
2012-05-14 09:06:42 +00:00
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_RBD
|
|
|
|
vshPrint(ctl, " RBD");
|
2012-07-18 19:06:58 +00:00
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_SHEEPDOG
|
|
|
|
vshPrint(ctl, " Sheepdog");
|
2013-12-12 03:08:10 +00:00
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_GLUSTER
|
|
|
|
vshPrint(ctl, " Gluster");
|
2016-01-03 02:25:35 +00:00
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_ZFS
|
|
|
|
vshPrint(ctl, " ZFS");
|
2017-01-17 14:10:55 +00:00
|
|
|
#endif
|
|
|
|
#ifdef WITH_STORAGE_VSTORAGE
|
|
|
|
vshPrint(ctl, "Virtuozzo Storage");
|
2010-11-08 14:03:32 +00:00
|
|
|
#endif
|
|
|
|
vshPrint(ctl, "\n");
|
|
|
|
|
2012-07-23 03:57:53 +00:00
|
|
|
vshPrint(ctl, "%s", _(" Miscellaneous:"));
|
2012-09-09 22:07:27 +00:00
|
|
|
#ifdef WITH_LIBVIRTD
|
|
|
|
vshPrint(ctl, " Daemon");
|
|
|
|
#endif
|
2012-07-23 03:57:53 +00:00
|
|
|
#ifdef WITH_NODE_DEVICES
|
|
|
|
vshPrint(ctl, " Nodedev");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_SECDRIVER_APPARMOR
|
|
|
|
vshPrint(ctl, " AppArmor");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_SECDRIVER_SELINUX
|
|
|
|
vshPrint(ctl, " SELinux");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_SECRETS
|
|
|
|
vshPrint(ctl, " Secrets");
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_DEBUG
|
|
|
|
vshPrint(ctl, " Debug");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_DTRACE_PROBES
|
|
|
|
vshPrint(ctl, " DTrace");
|
|
|
|
#endif
|
2013-10-04 17:51:41 +00:00
|
|
|
#if WITH_READLINE
|
2012-07-23 03:57:53 +00:00
|
|
|
vshPrint(ctl, " Readline");
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_DRIVER_MODULES
|
|
|
|
vshPrint(ctl, " Modular");
|
|
|
|
#endif
|
|
|
|
vshPrint(ctl, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2015-06-15 16:53:58 +00:00
|
|
|
virshAllowedEscapeChar(char c)
|
2012-07-23 03:57:53 +00:00
|
|
|
{
|
|
|
|
/* Allowed escape characters:
|
|
|
|
* a-z A-Z @ [ \ ] ^ _
|
|
|
|
*/
|
|
|
|
return ('a' <= c && c <= 'z') ||
|
|
|
|
('@' <= c && c <= '_');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* argv[]: virsh [options] [command]
|
|
|
|
*
|
|
|
|
*/
|
virsh: use common namespacing
Convert the exported items in virsh.h to use a common 'vsh' prefix.
* tools/virsh.h (VIRSH_MAX_XML_FILE): Rename...
(VSH_MAX_XML_FILE): ...and parenthesize.
(DIFF_MSEC, CTRL_CLOSE_BRACKET): Delete.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Remove prototype.
(editWriteToTempFile, editFile, editReadBackFile, prettyCapacity)
(virshReportError): Rename...
(vshEditWriteToTempFile, vshEditFile, vshEditReadBackFile)
(vshPrettyCapacity, vshReportError): ...into vsh namespace.
(jobWatchTimeoutFunc): Move to virsh-domain.c.
* tools/virsh.c (vshCommandRun): Inline former DIFF_MSEC.
(main): Inline former CTRL_CLOSE_BRACKET.
(vshUsage, vshInit, vshDeinit, vshParseArgv): Make static.
(prettyCapacity, virshReportError, editWriteToTempFile, editFile):
Fix naming, and adjust usage.
(vshAskReedit, vshCommandRun, vshEventLoop, vshInit): Adjust
usage.
* tools/virsh-domain.c (cmdAttachDevice, cmdCPUCompare)
(cmdCPUBaseline, cmdCreate, cmdDefine, cmdDetachDevice)
(cmdUpdateDevice, cmdDesc, cmdUndefine, cmdStart, cmdVcpucount)
(cmdAttachDevice, cmdDomjobinfo): Likewise.
* tools/virsh-edit.c (do): Likewise.
* tools/virsh-interface.c (cmdInterfaceDefine): Likewise.
* tools/virsh-network.c (cmdNetworkCreate, cmdNetworkDefine):
Likewise.
* tools/virsh-nodedev.c (cmdNodeDeviceCreate): Likewise.
* tools/virsh-nwfilter.c (cmdNWFilterDefine): Likewise.
* tools/virsh-pool.c (cmdPoolCreate, cmdPoolDefine)
(cmdPoolDiscoverSources, cmdPoolList): Likewise.
* tools/virsh-secret.c (cmdSecretDefine): Likewise.
* tools/virsh-snapshot.c (cmdSnapshotCreate, vshSnapshotCreate)
(vshLookupSnapshot, cmdSnapshotEdit, cmdSnapshotCurrent)
(vshGetSnapshotParent): Likewise.
* tools/virsh-volume.c (cmdVolCreate, cmdVolCreateFrom)
(cmdVolInfo, cmdVolList): Likewise.
2012-08-19 04:10:17 +00:00
|
|
|
static bool
|
2015-06-15 16:53:58 +00:00
|
|
|
virshParseArgv(vshControl *ctl, int argc, char **argv)
|
2012-07-23 03:57:53 +00:00
|
|
|
{
|
2014-03-06 16:20:11 +00:00
|
|
|
int arg, len, debug, keepalive;
|
Convert 'int i' to 'size_t i' in tools/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-04-29 17:12:17 +00:00
|
|
|
int longindex = -1;
|
2015-06-15 16:53:58 +00:00
|
|
|
virshControlPtr priv = ctl->privData;
|
2012-07-23 03:57:53 +00:00
|
|
|
struct option opt[] = {
|
2014-03-06 15:53:53 +00:00
|
|
|
{"connect", required_argument, NULL, 'c'},
|
2012-07-23 03:57:53 +00:00
|
|
|
{"debug", required_argument, NULL, 'd'},
|
2014-03-06 15:53:53 +00:00
|
|
|
{"escape", required_argument, NULL, 'e'},
|
2012-07-23 03:57:53 +00:00
|
|
|
{"help", no_argument, NULL, 'h'},
|
2014-03-06 16:20:11 +00:00
|
|
|
{"keepalive-interval", required_argument, NULL, 'k'},
|
|
|
|
{"keepalive-count", required_argument, NULL, 'K'},
|
2014-03-06 15:53:53 +00:00
|
|
|
{"log", required_argument, NULL, 'l'},
|
2012-07-23 03:57:53 +00:00
|
|
|
{"quiet", no_argument, NULL, 'q'},
|
2014-03-06 15:53:53 +00:00
|
|
|
{"readonly", no_argument, NULL, 'r'},
|
2012-07-23 03:57:53 +00:00
|
|
|
{"timing", no_argument, NULL, 't'},
|
|
|
|
{"version", optional_argument, NULL, 'v'},
|
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Standard (non-command) options. The leading + ensures that no
|
|
|
|
* argument reordering takes place, so that command options are
|
|
|
|
* not confused with top-level virsh options. */
|
2014-03-06 16:20:11 +00:00
|
|
|
while ((arg = getopt_long(argc, argv, "+:c:d:e:hk:K:l:qrtvV", opt, &longindex)) != -1) {
|
2012-07-23 03:57:53 +00:00
|
|
|
switch (arg) {
|
2014-03-06 15:53:53 +00:00
|
|
|
case 'c':
|
2015-12-10 12:46:45 +00:00
|
|
|
VIR_FREE(ctl->connname);
|
|
|
|
ctl->connname = vshStrdup(ctl, optarg);
|
2014-03-06 15:53:53 +00:00
|
|
|
break;
|
2012-07-23 03:57:53 +00:00
|
|
|
case 'd':
|
2012-07-26 14:05:51 +00:00
|
|
|
if (virStrToLong_i(optarg, NULL, 10, &debug) < 0) {
|
2013-04-29 17:12:17 +00:00
|
|
|
vshError(ctl, _("option %s takes a numeric argument"),
|
|
|
|
longindex == -1 ? "-d" : "--debug");
|
2012-07-23 03:57:53 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2012-07-26 14:05:51 +00:00
|
|
|
if (debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR)
|
|
|
|
vshError(ctl, _("ignoring debug level %d out of range [%d-%d]"),
|
|
|
|
debug, VSH_ERR_DEBUG, VSH_ERR_ERROR);
|
|
|
|
else
|
|
|
|
ctl->debug = debug;
|
2012-07-23 03:57:53 +00:00
|
|
|
break;
|
2014-03-06 15:53:53 +00:00
|
|
|
case 'e':
|
|
|
|
len = strlen(optarg);
|
|
|
|
|
|
|
|
if ((len == 2 && *optarg == '^' &&
|
2015-06-15 16:53:58 +00:00
|
|
|
virshAllowedEscapeChar(optarg[1])) ||
|
2014-03-06 15:53:53 +00:00
|
|
|
(len == 1 && *optarg != '^')) {
|
2015-06-15 16:53:58 +00:00
|
|
|
priv->escapeChar = optarg;
|
2014-03-06 15:53:53 +00:00
|
|
|
} else {
|
|
|
|
vshError(ctl, _("Invalid string '%s' for escape sequence"),
|
|
|
|
optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
break;
|
2012-07-23 03:57:53 +00:00
|
|
|
case 'h':
|
2015-06-15 16:53:58 +00:00
|
|
|
virshUsage();
|
2012-07-23 03:57:53 +00:00
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
break;
|
2014-03-06 16:20:11 +00:00
|
|
|
case 'k':
|
2014-08-27 14:20:29 +00:00
|
|
|
if (virStrToLong_i(optarg, NULL, 0, &keepalive) < 0) {
|
|
|
|
vshError(ctl,
|
|
|
|
_("Invalid value for option %s"),
|
|
|
|
longindex == -1 ? "-k" : "--keepalive-interval");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keepalive < 0) {
|
|
|
|
vshError(ctl,
|
|
|
|
_("option %s requires a positive integer argument"),
|
2014-03-06 16:20:11 +00:00
|
|
|
longindex == -1 ? "-k" : "--keepalive-interval");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
ctl->keepalive_interval = keepalive;
|
|
|
|
break;
|
|
|
|
case 'K':
|
2014-08-27 14:20:29 +00:00
|
|
|
if (virStrToLong_i(optarg, NULL, 0, &keepalive) < 0) {
|
|
|
|
vshError(ctl,
|
|
|
|
_("Invalid value for option %s"),
|
|
|
|
longindex == -1 ? "-K" : "--keepalive-count");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keepalive < 0) {
|
|
|
|
vshError(ctl,
|
|
|
|
_("option %s requires a positive integer argument"),
|
2014-03-06 16:20:11 +00:00
|
|
|
longindex == -1 ? "-K" : "--keepalive-count");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
ctl->keepalive_count = keepalive;
|
|
|
|
break;
|
2014-03-06 15:53:53 +00:00
|
|
|
case 'l':
|
|
|
|
vshCloseLogFile(ctl);
|
|
|
|
ctl->logfile = vshStrdup(ctl, optarg);
|
|
|
|
vshOpenLogFile(ctl);
|
|
|
|
break;
|
2012-07-23 03:57:53 +00:00
|
|
|
case 'q':
|
|
|
|
ctl->quiet = true;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
ctl->timing = true;
|
|
|
|
break;
|
2014-03-06 15:53:53 +00:00
|
|
|
case 'r':
|
2015-06-15 16:53:58 +00:00
|
|
|
priv->readonly = true;
|
2012-07-23 03:57:53 +00:00
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
if (STRNEQ_NULLABLE(optarg, "long")) {
|
|
|
|
puts(VERSION);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
case 'V':
|
2015-06-15 16:53:58 +00:00
|
|
|
virshShowVersion(ctl);
|
2012-07-23 03:57:53 +00:00
|
|
|
exit(EXIT_SUCCESS);
|
2013-02-19 08:50:59 +00:00
|
|
|
case ':':
|
2013-04-29 17:12:17 +00:00
|
|
|
for (i = 0; opt[i].name != NULL; i++) {
|
2013-05-01 15:07:56 +00:00
|
|
|
if (opt[i].val == optopt)
|
|
|
|
break;
|
2013-04-29 17:12:17 +00:00
|
|
|
}
|
2013-05-01 15:07:56 +00:00
|
|
|
if (opt[i].name)
|
|
|
|
vshError(ctl, _("option '-%c'/'--%s' requires an argument"),
|
|
|
|
optopt, opt[i].name);
|
|
|
|
else
|
|
|
|
vshError(ctl, _("option '-%c' requires an argument"), optopt);
|
|
|
|
exit(EXIT_FAILURE);
|
2013-02-19 08:50:59 +00:00
|
|
|
case '?':
|
2013-04-29 17:12:17 +00:00
|
|
|
if (optopt)
|
|
|
|
vshError(ctl, _("unsupported option '-%c'. See --help."), optopt);
|
|
|
|
else
|
|
|
|
vshError(ctl, _("unsupported option '%s'. See --help."), argv[optind - 1]);
|
2013-02-19 08:50:59 +00:00
|
|
|
exit(EXIT_FAILURE);
|
2012-07-23 03:57:53 +00:00
|
|
|
default:
|
2013-02-19 08:50:59 +00:00
|
|
|
vshError(ctl, _("unknown option"));
|
2012-07-23 03:57:53 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2013-04-29 17:12:17 +00:00
|
|
|
longindex = -1;
|
2012-07-23 03:57:53 +00:00
|
|
|
}
|
|
|
|
|
2015-09-04 09:19:55 +00:00
|
|
|
if (argc == optind) {
|
|
|
|
ctl->imode = true;
|
|
|
|
} else {
|
2012-07-23 03:57:53 +00:00
|
|
|
/* parse command */
|
|
|
|
ctl->imode = false;
|
|
|
|
if (argc - optind == 1) {
|
|
|
|
vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]);
|
|
|
|
return vshCommandStringParse(ctl, argv[optind]);
|
|
|
|
} else {
|
|
|
|
return vshCommandArgvParse(ctl, argc - optind, argv + optind);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const vshCmdDef virshCmds[] = {
|
2015-06-15 16:53:58 +00:00
|
|
|
VSH_CMD_CD,
|
|
|
|
VSH_CMD_ECHO,
|
|
|
|
VSH_CMD_EXIT,
|
|
|
|
VSH_CMD_HELP,
|
|
|
|
VSH_CMD_PWD,
|
|
|
|
VSH_CMD_QUIT,
|
2016-09-14 07:50:22 +00:00
|
|
|
VSH_CMD_SELF_TEST,
|
2013-03-27 13:22:47 +00:00
|
|
|
{.name = "connect",
|
|
|
|
.handler = cmdConnect,
|
|
|
|
.opts = opts_connect,
|
|
|
|
.info = info_connect,
|
|
|
|
.flags = VSH_CMD_FLAG_NOCONNECT
|
2013-02-07 15:25:10 +00:00
|
|
|
},
|
|
|
|
{.name = NULL}
|
2012-07-23 03:57:53 +00:00
|
|
|
};
|
2011-11-22 16:08:05 +00:00
|
|
|
|
2012-07-23 03:57:53 +00:00
|
|
|
static const vshCmdGrp cmdGroups[] = {
|
2015-06-15 16:53:58 +00:00
|
|
|
{VIRSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds},
|
|
|
|
{VIRSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds},
|
|
|
|
{VIRSH_CMD_GRP_HOST_AND_HV, "host", hostAndHypervisorCmds},
|
|
|
|
{VIRSH_CMD_GRP_IFACE, "interface", ifaceCmds},
|
|
|
|
{VIRSH_CMD_GRP_NWFILTER, "filter", nwfilterCmds},
|
|
|
|
{VIRSH_CMD_GRP_NETWORK, "network", networkCmds},
|
|
|
|
{VIRSH_CMD_GRP_NODEDEV, "nodedev", nodedevCmds},
|
|
|
|
{VIRSH_CMD_GRP_SECRET, "secret", secretCmds},
|
|
|
|
{VIRSH_CMD_GRP_SNAPSHOT, "snapshot", snapshotCmds},
|
|
|
|
{VIRSH_CMD_GRP_STORAGE_POOL, "pool", storagePoolCmds},
|
|
|
|
{VIRSH_CMD_GRP_STORAGE_VOL, "volume", storageVolCmds},
|
|
|
|
{VIRSH_CMD_GRP_VIRSH, "virsh", virshCmds},
|
2012-07-23 03:57:53 +00:00
|
|
|
{NULL, NULL, NULL}
|
|
|
|
};
|
2005-12-08 10:23:34 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
static const vshClientHooks hooks = {
|
|
|
|
.connHandler = virshConnectionHandler
|
|
|
|
};
|
|
|
|
|
2006-03-15 12:13:25 +00:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
vshControl _ctl, *ctl = &_ctl;
|
2015-06-15 16:53:58 +00:00
|
|
|
virshControl virshCtl;
|
2011-04-18 22:37:42 +00:00
|
|
|
bool ret = true;
|
2005-12-08 10:23:34 +00:00
|
|
|
|
2011-05-09 11:57:09 +00:00
|
|
|
memset(ctl, 0, sizeof(vshControl));
|
2015-06-15 16:53:58 +00:00
|
|
|
memset(&virshCtl, 0, sizeof(virshControl));
|
|
|
|
ctl->name = "virsh"; /* hardcoded name of the binary */
|
2016-07-28 10:54:16 +00:00
|
|
|
ctl->env_prefix = "VIRSH";
|
2011-05-09 11:57:09 +00:00
|
|
|
ctl->log_fd = -1; /* Initialize log file descriptor */
|
2011-07-14 11:58:02 +00:00
|
|
|
ctl->debug = VSH_DEBUG_DEFAULT;
|
2015-06-15 16:53:58 +00:00
|
|
|
ctl->hooks = &hooks;
|
2014-03-06 16:20:11 +00:00
|
|
|
|
|
|
|
/* In order to distinguish default from setting to 0 */
|
|
|
|
ctl->keepalive_interval = -1;
|
|
|
|
ctl->keepalive_count = -1;
|
|
|
|
|
virsh: common code for waiting for an event
I plan to add 'virsh event' to virsh-domain.c and 'virsh
net-event' to virsh-network.c; but as they will share quite
a bit of common boilerplate, it's better to set that up now
in virsh.c.
* tools/virsh.h (_vshControl): Add fields.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup): New
prototypes.
* tools/virsh.c (vshEventFd, vshEventOldAction, vshEventInt)
(vshEventTimeout): New helper variables and functions.
(vshEventStart, vshEventWait, vshEventDone, vshEventCleanup):
Implement new functions.
(vshInit, vshDeinit, main): Manage event timeout.
Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-14 22:30:23 +00:00
|
|
|
ctl->eventPipe[0] = -1;
|
|
|
|
ctl->eventPipe[1] = -1;
|
|
|
|
ctl->eventTimerId = -1;
|
2015-06-15 16:53:58 +00:00
|
|
|
virshCtl.escapeChar = "^]"; /* Same default as telnet */
|
|
|
|
ctl->privData = &virshCtl;
|
|
|
|
|
|
|
|
if (!(progname = strrchr(argv[0], '/')))
|
|
|
|
progname = argv[0];
|
|
|
|
else
|
|
|
|
progname++;
|
|
|
|
ctl->progname = progname;
|
2011-05-09 11:57:09 +00:00
|
|
|
|
2016-04-12 22:29:52 +00:00
|
|
|
if (virGettextInitialize() < 0)
|
2010-11-16 19:01:37 +00:00
|
|
|
return EXIT_FAILURE;
|
2006-09-21 15:24:37 +00:00
|
|
|
|
2013-08-29 08:36:00 +00:00
|
|
|
if (isatty(STDIN_FILENO)) {
|
|
|
|
ctl->istty = true;
|
|
|
|
|
2013-09-04 21:57:30 +00:00
|
|
|
#ifndef WIN32
|
2013-08-29 08:36:00 +00:00
|
|
|
if (tcgetattr(STDIN_FILENO, &ctl->termattr) < 0)
|
|
|
|
ctl->istty = false;
|
2013-09-04 21:57:30 +00:00
|
|
|
#endif
|
2013-08-29 08:36:00 +00:00
|
|
|
}
|
|
|
|
|
2011-11-30 19:42:20 +00:00
|
|
|
if (virMutexInit(&ctl->lock) < 0) {
|
|
|
|
vshError(ctl, "%s", _("Failed to initialize mutex"));
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2011-05-09 11:57:09 +00:00
|
|
|
if (virInitialize() < 0) {
|
|
|
|
vshError(ctl, "%s", _("Failed to initialize libvirt"));
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-04-24 14:57:36 +00:00
|
|
|
virFileActivateDirOverride(argv[0]);
|
|
|
|
|
2015-09-03 14:52:44 +00:00
|
|
|
if (!vshInit(ctl, cmdGroups, NULL))
|
2015-06-15 16:53:58 +00:00
|
|
|
exit(EXIT_FAILURE);
|
2006-03-15 12:13:25 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
if (!virshParseArgv(ctl, argc, argv) ||
|
|
|
|
!virshInit(ctl)) {
|
|
|
|
virshDeinit(ctl);
|
2005-12-08 10:23:34 +00:00
|
|
|
exit(EXIT_FAILURE);
|
2007-12-01 15:45:25 +00:00
|
|
|
}
|
2006-03-15 12:13:25 +00:00
|
|
|
|
2016-04-21 08:26:31 +00:00
|
|
|
if (!ctl->connname)
|
|
|
|
ctl->connname = vshStrdup(ctl,
|
|
|
|
virGetEnvBlockSUID("VIRSH_DEFAULT_CONNECT_URI"));
|
|
|
|
|
2005-12-08 10:23:34 +00:00
|
|
|
if (!ctl->imode) {
|
2006-03-15 12:13:25 +00:00
|
|
|
ret = vshCommandRun(ctl, ctl->cmd);
|
2005-12-01 16:35:42 +00:00
|
|
|
} else {
|
2005-12-08 10:23:34 +00:00
|
|
|
/* interactive mode */
|
|
|
|
if (!ctl->quiet) {
|
2006-05-22 14:38:33 +00:00
|
|
|
vshPrint(ctl,
|
2006-09-21 15:24:37 +00:00
|
|
|
_("Welcome to %s, the virtualization interactive terminal.\n\n"),
|
2006-03-15 12:13:25 +00:00
|
|
|
progname);
|
2008-01-16 17:13:23 +00:00
|
|
|
vshPrint(ctl, "%s",
|
2006-09-21 15:24:37 +00:00
|
|
|
_("Type: 'help' for help with commands\n"
|
2007-02-07 13:50:18 +00:00
|
|
|
" 'quit' to quit\n\n"));
|
2005-12-08 10:23:34 +00:00
|
|
|
}
|
2010-01-03 14:45:10 +00:00
|
|
|
|
2005-12-08 10:23:34 +00:00
|
|
|
do {
|
2015-06-15 16:53:58 +00:00
|
|
|
const char *prompt = virshCtl.readonly ? VIRSH_PROMPT_RO
|
|
|
|
: VIRSH_PROMPT_RW;
|
2006-03-15 12:13:25 +00:00
|
|
|
ctl->cmdstr =
|
2007-12-06 16:36:21 +00:00
|
|
|
vshReadline(ctl, prompt);
|
2006-03-15 12:13:25 +00:00
|
|
|
if (ctl->cmdstr == NULL)
|
|
|
|
break; /* EOF */
|
2005-12-08 10:23:34 +00:00
|
|
|
if (*ctl->cmdstr) {
|
2013-10-04 17:51:41 +00:00
|
|
|
#if WITH_READLINE
|
2005-12-08 10:23:34 +00:00
|
|
|
add_history(ctl->cmdstr);
|
2007-12-04 18:27:52 +00:00
|
|
|
#endif
|
2010-10-12 07:13:50 +00:00
|
|
|
if (vshCommandStringParse(ctl, ctl->cmdstr))
|
2005-12-08 10:23:34 +00:00
|
|
|
vshCommandRun(ctl, ctl->cmd);
|
|
|
|
}
|
2010-01-03 16:13:27 +00:00
|
|
|
VIR_FREE(ctl->cmdstr);
|
2006-03-15 12:13:25 +00:00
|
|
|
} while (ctl->imode);
|
2005-12-08 10:23:34 +00:00
|
|
|
|
2006-03-15 12:13:25 +00:00
|
|
|
if (ctl->cmdstr == NULL)
|
|
|
|
fputc('\n', stdout); /* line break after alone prompt */
|
2005-12-08 10:23:34 +00:00
|
|
|
}
|
2006-03-15 12:13:25 +00:00
|
|
|
|
2015-06-15 16:53:58 +00:00
|
|
|
virshDeinit(ctl);
|
2005-12-08 10:23:34 +00:00
|
|
|
exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
|
2005-11-10 16:12:31 +00:00
|
|
|
}
|