I talked to Martin and Michal about this and I agree with them that
moving the command structures as well as the commands is better idea
than leaving the structures in virsh.c so I'll send a v4 inline patch.
Erik
On 13/08/15 13:39, Erik Skultety wrote:
Generic commands like 'help', 'cd',
'pwd',etc. can be reused by any
client, so the clients should profit from this implementation rather
than providing their own similar implementation (if it's not intensional
and there's a reason for this)
---
tools/virsh.c | 343 +++++++++++++++-------------------------------------------
tools/vsh.c | 155 ++++++++++++++++++++++++++
tools/vsh.h | 7 ++
3 files changed, 250 insertions(+), 255 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 2d198cd..e123ea2 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -188,79 +188,6 @@ virshReconnect(vshControl *ctl)
priv->blockJobNoBytes = false;
}
-
-/*
- * "connect" command
- */
-static const vshCmdInfo info_connect[] = {
- {.name = "help",
- .data = N_("(re)connect to hypervisor")
- },
- {.name = "desc",
- .data = N_("Connect to local hypervisor. This is built-in "
- "command after shell start up.")
- },
- {.name = NULL}
-};
-
-static const vshCmdOptDef opts_connect[] = {
- {.name = "name",
- .type = VSH_OT_STRING,
- .flags = VSH_OFLAG_EMPTY_OK,
- .help = N_("hypervisor connection URI")
- },
- {.name = "readonly",
- .type = VSH_OT_BOOL,
- .help = N_("read-only connection")
- },
- {.name = NULL}
-};
-
-static bool
-cmdConnect(vshControl *ctl, const vshCmd *cmd)
-{
- bool ro = vshCommandOptBool(cmd, "readonly");
- const char *name = NULL;
- virshControlPtr priv = ctl->privData;
-
- if (priv->conn) {
- int ret;
-
- virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect);
- ret = virConnectClose(priv->conn);
- 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"));
- priv->conn = NULL;
- }
-
- VIR_FREE(ctl->name);
- if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0)
- return false;
-
- ctl->name = vshStrdup(ctl, name);
-
- priv->useGetInfo = false;
- priv->useSnapshotOld = false;
- priv->blockJobNoBytes = false;
- priv->readonly = ro;
-
- priv->conn = virshConnect(ctl, ctl->name, priv->readonly);
-
- if (!priv->conn) {
- vshError(ctl, "%s", _("Failed to connect to the
hypervisor"));
- return false;
- }
-
- if (virConnectRegisterCloseCallback(priv->conn, virshCatchDisconnect,
- NULL, NULL) < 0)
- vshError(ctl, "%s", _("Unable to register disconnect
callback"));
-
- return true;
-}
-
int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
const char *bytes, size_t nbytes, void *opaque)
{
@@ -270,13 +197,9 @@ int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
}
/* ---------------
- * Commands
+ * Command Info
* ---------------
*/
-
-/*
- * "help" command
- */
static const vshCmdInfo info_help[] = {
{.name = "help",
.data = N_("print help")
@@ -288,55 +211,6 @@ static const vshCmdInfo info_help[] = {
{.name = NULL}
};
-static const vshCmdOptDef opts_help[] = {
- {.name = "command",
- .type = VSH_OT_STRING,
- .help = N_("Prints global help, command specific help, or help for a group of
related commands")
- },
- {.name = NULL}
-};
-
-static bool
-cmdHelp(vshControl *ctl, const vshCmd *cmd)
- {
- const char *name = NULL;
-
- if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) {
- const vshCmdGrp *grp;
- const vshCmdDef *def;
-
- vshPrint(ctl, "%s", _("Grouped commands:\n\n"));
-
- for (grp = cmdGroups; grp->name; grp++) {
- vshPrint(ctl, _(" %s (help keyword '%s'):\n"),
grp->name,
- grp->keyword);
-
- for (def = grp->commands; def->name; def++) {
- if (def->flags & VSH_CMD_FLAG_ALIAS)
- continue;
- vshPrint(ctl, " %-30s %s\n", def->name,
- _(vshCmddefGetInfo(def, "help")));
- }
-
- vshPrint(ctl, "\n");
- }
-
- return true;
- }
-
- if (vshCmddefSearch(name)) {
- return vshCmddefHelp(ctl, name);
- } else if (vshCmdGrpSearch(name)) {
- return vshCmdGrpHelp(ctl, name);
- } else {
- vshError(ctl, _("command or command group '%s' doesn't
exist"), name);
- return false;
- }
-}
-
-/*
- * "cd" command
- */
static const vshCmdInfo info_cd[] = {
{.name = "help",
.data = N_("change the current directory")
@@ -347,45 +221,6 @@ static const vshCmdInfo info_cd[] = {
{.name = NULL}
};
-static const vshCmdOptDef opts_cd[] = {
- {.name = "dir",
- .type = VSH_OT_STRING,
- .help = N_("directory to switch to (default: home or else root)")
- },
- {.name = NULL}
-};
-
-static bool
-cmdCd(vshControl *ctl, const vshCmd *cmd)
-{
- const char *dir = NULL;
- char *dir_malloced = NULL;
- bool ret = true;
- char ebuf[1024];
-
- if (!ctl->imode) {
- vshError(ctl, "%s", _("cd: command valid only in interactive
mode"));
- return false;
- }
-
- if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0)
- dir = dir_malloced = virGetUserDirectory();
- if (!dir)
- dir = "/";
-
- if (chdir(dir) == -1) {
- vshError(ctl, _("cd: %s: %s"),
- virStrerror(errno, ebuf, sizeof(ebuf)), dir);
- ret = false;
- }
-
- VIR_FREE(dir_malloced);
- return ret;
-}
-
-/*
- * "pwd" command
- */
static const vshCmdInfo info_pwd[] = {
{.name = "help",
.data = N_("print the current directory")
@@ -396,29 +231,6 @@ static const vshCmdInfo info_pwd[] = {
{.name = NULL}
};
-static bool
-cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- char *cwd;
- bool ret = true;
- char ebuf[1024];
-
- cwd = getcwd(NULL, 0);
- if (!cwd) {
- vshError(ctl, _("pwd: cannot get current directory: %s"),
- virStrerror(errno, ebuf, sizeof(ebuf)));
- ret = false;
- } else {
- vshPrint(ctl, _("%s\n"), cwd);
- VIR_FREE(cwd);
- }
-
- return ret;
-}
-
-/*
- * "echo" command
- */
static const vshCmdInfo info_echo[] = {
{.name = "help",
.data = N_("echo arguments")
@@ -429,6 +241,47 @@ static const vshCmdInfo info_echo[] = {
{.name = NULL}
};
+static const vshCmdInfo info_quit[] = {
+ {.name = "help",
+ .data = N_("quit this interactive terminal")
+ },
+ {.name = "desc",
+ .data = ""
+ },
+ {.name = NULL}
+};
+
+static const vshCmdInfo info_connect[] = {
+ {.name = "help",
+ .data = N_("(re)connect to hypervisor")
+ },
+ {.name = "desc",
+ .data = N_("Connect to local hypervisor. This is built-in "
+ "command after shell start up.")
+ },
+ {.name = NULL}
+};
+
+/* ---------------
+ * Command Opts
+ * ---------------
+ */
+static const vshCmdOptDef opts_help[] = {
+ {.name = "command",
+ .type = VSH_OT_STRING,
+ .help = N_("Prints global help, command specific help, or help for a group of
related commands")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_cd[] = {
+ {.name = "dir",
+ .type = VSH_OT_STRING,
+ .help = N_("directory to switch to (default: home or else root)")
+ },
+ {.name = NULL}
+};
+
static const vshCmdOptDef opts_echo[] = {
{.name = "shell",
.type = VSH_OT_BOOL,
@@ -453,80 +306,61 @@ static const vshCmdOptDef opts_echo[] = {
{.name = NULL}
};
-/* Exists mainly for debugging virsh, but also handy for adding back
- * quotes for later evaluation.
- */
+static const vshCmdOptDef opts_connect[] = {
+ {.name = "name",
+ .type = VSH_OT_STRING,
+ .flags = VSH_OFLAG_EMPTY_OK,
+ .help = N_("hypervisor connection URI")
+ },
+ {.name = "readonly",
+ .type = VSH_OT_BOOL,
+ .help = N_("read-only connection")
+ },
+ {.name = NULL}
+};
+
static bool
-cmdEcho(vshControl *ctl, const vshCmd *cmd)
+cmdConnect(vshControl *ctl, const vshCmd *cmd)
{
- bool shell = false;
- bool xml = false;
- int count = 0;
- const vshCmdOpt *opt = NULL;
- char *arg;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
-
- if (vshCommandOptBool(cmd, "shell"))
- shell = true;
- if (vshCommandOptBool(cmd, "xml"))
- xml = true;
-
- while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
- char *str;
- virBuffer xmlbuf = VIR_BUFFER_INITIALIZER;
-
- arg = opt->data;
-
- if (count)
- virBufferAddChar(&buf, ' ');
-
- if (xml) {
- virBufferEscapeString(&xmlbuf, "%s", arg);
- if (virBufferError(&xmlbuf)) {
- vshPrint(ctl, "%s", _("Failed to allocate XML
buffer"));
- return false;
- }
- str = virBufferContentAndReset(&xmlbuf);
- } else {
- str = vshStrdup(ctl, arg);
- }
+ bool ro = vshCommandOptBool(cmd, "readonly");
+ const char *name = NULL;
+ virshControlPtr priv = ctl->privData;
- if (shell)
- virBufferEscapeShell(&buf, str);
- else
- virBufferAdd(&buf, str, -1);
- count++;
- VIR_FREE(str);
+ if (priv->conn) {
+ int ret;
+
+ virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect);
+ ret = virConnectClose(priv->conn);
+ 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"));
+ priv->conn = NULL;
}
- if (virBufferError(&buf)) {
- vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+ VIR_FREE(ctl->connname);
+ if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0)
+ return false;
+
+ ctl->connname = vshStrdup(ctl, name);
+
+ priv->useGetInfo = false;
+ priv->useSnapshotOld = false;
+ priv->blockJobNoBytes = false;
+ priv->readonly = ro;
+
+ priv->conn = virshConnect(ctl, ctl->connname, priv->readonly);
+
+ if (!priv->conn) {
+ vshError(ctl, "%s", _("Failed to connect to the
hypervisor"));
return false;
}
- arg = virBufferContentAndReset(&buf);
- if (arg)
- vshPrint(ctl, "%s", arg);
- VIR_FREE(arg);
- return true;
-}
-/*
- * "quit" command
- */
-static const vshCmdInfo info_quit[] = {
- {.name = "help",
- .data = N_("quit this interactive terminal")
- },
- {.name = "desc",
- .data = ""
- },
- {.name = NULL}
-};
+ if (virConnectRegisterCloseCallback(priv->conn, virshCatchDisconnect,
+ NULL, NULL) < 0)
+ vshError(ctl, "%s", _("Unable to register disconnect
callback"));
-static bool
-cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- ctl->imode = false;
return true;
}
@@ -535,7 +369,6 @@ cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
* ---------------
*/
-
static bool
virshConnectionUsability(vshControl *ctl, virConnectPtr conn)
{
diff --git a/tools/vsh.c b/tools/vsh.c
index 043d0fb..084cac5 100644
--- a/tools/vsh.c
+++ b/tools/vsh.c
@@ -2748,3 +2748,158 @@ vshDeinit(vshControl *ctl)
vshReadlineDeinit(ctl);
vshCloseLogFile(ctl);
}
+
+/* -----------------------------------------------
+ * Generic commands available to use by any client
+ * -----------------------------------------------
+ */
+
+bool
+cmdHelp(vshControl *ctl, const vshCmd *cmd)
+ {
+ const char *name = NULL;
+
+ if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) {
+ const vshCmdGrp *grp;
+ const vshCmdDef *def;
+
+ vshPrint(ctl, "%s", _("Grouped commands:\n\n"));
+
+ for (grp = cmdGroups; grp->name; grp++) {
+ vshPrint(ctl, _(" %s (help keyword '%s'):\n"),
grp->name,
+ grp->keyword);
+
+ for (def = grp->commands; def->name; def++) {
+ if (def->flags & VSH_CMD_FLAG_ALIAS)
+ continue;
+ vshPrint(ctl, " %-30s %s\n", def->name,
+ _(vshCmddefGetInfo(def, "help")));
+ }
+
+ vshPrint(ctl, "\n");
+ }
+
+ return true;
+ }
+
+ if (vshCmddefSearch(name)) {
+ return vshCmddefHelp(ctl, name);
+ } else if (vshCmdGrpSearch(name)) {
+ return vshCmdGrpHelp(ctl, name);
+ } else {
+ vshError(ctl, _("command or command group '%s' doesn't
exist"), name);
+ return false;
+ }
+}
+
+bool
+cmdCd(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *dir = NULL;
+ char *dir_malloced = NULL;
+ bool ret = true;
+ char ebuf[1024];
+
+ if (!ctl->imode) {
+ vshError(ctl, "%s", _("cd: command valid only in interactive
mode"));
+ return false;
+ }
+
+ if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0)
+ dir = dir_malloced = virGetUserDirectory();
+ if (!dir)
+ dir = "/";
+
+ if (chdir(dir) == -1) {
+ vshError(ctl, _("cd: %s: %s"),
+ virStrerror(errno, ebuf, sizeof(ebuf)), dir);
+ ret = false;
+ }
+
+ VIR_FREE(dir_malloced);
+ return ret;
+}
+
+/* Exists mainly for debugging virsh, but also handy for adding back
+ * quotes for later evaluation.
+ */
+bool
+cmdEcho(vshControl *ctl, const vshCmd *cmd)
+{
+ bool shell = false;
+ bool xml = false;
+ int count = 0;
+ const vshCmdOpt *opt = NULL;
+ char *arg;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (vshCommandOptBool(cmd, "shell"))
+ shell = true;
+ if (vshCommandOptBool(cmd, "xml"))
+ xml = true;
+
+ while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
+ char *str;
+ virBuffer xmlbuf = VIR_BUFFER_INITIALIZER;
+
+ arg = opt->data;
+
+ if (count)
+ virBufferAddChar(&buf, ' ');
+
+ if (xml) {
+ virBufferEscapeString(&xmlbuf, "%s", arg);
+ if (virBufferError(&xmlbuf)) {
+ vshPrint(ctl, "%s", _("Failed to allocate XML
buffer"));
+ return false;
+ }
+ str = virBufferContentAndReset(&xmlbuf);
+ } else {
+ str = vshStrdup(ctl, arg);
+ }
+
+ if (shell)
+ virBufferEscapeShell(&buf, str);
+ else
+ virBufferAdd(&buf, str, -1);
+ count++;
+ VIR_FREE(str);
+ }
+
+ if (virBufferError(&buf)) {
+ vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+ return false;
+ }
+ arg = virBufferContentAndReset(&buf);
+ if (arg)
+ vshPrint(ctl, "%s", arg);
+ VIR_FREE(arg);
+ return true;
+}
+
+bool
+cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ char *cwd;
+ bool ret = true;
+ char ebuf[1024];
+
+ cwd = getcwd(NULL, 0);
+ if (!cwd) {
+ vshError(ctl, _("pwd: cannot get current directory: %s"),
+ virStrerror(errno, ebuf, sizeof(ebuf)));
+ ret = false;
+ } else {
+ vshPrint(ctl, _("%s\n"), cwd);
+ VIR_FREE(cwd);
+ }
+
+ return ret;
+}
+
+bool
+cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ ctl->imode = false;
+ return true;
+}
diff --git a/tools/vsh.h b/tools/vsh.h
index 19862d0..cbc3455 100644
--- a/tools/vsh.h
+++ b/tools/vsh.h
@@ -363,6 +363,13 @@ int vshEventStart(vshControl *ctl, int timeout_ms);
void vshEventTimeout(int timer, void *opaque);
int vshEventWait(vshControl *ctl);
+/* generic commands */
+bool cmdHelp(vshControl *ctl, const vshCmd *cmd);
+bool cmdCd(vshControl *ctl, const vshCmd *cmd);
+bool cmdEcho(vshControl *ctl, const vshCmd *cmd);
+bool cmdPwd(vshControl *ctl, const vshCmd *cmd);
+bool cmdQuit(vshControl *ctl, const vshCmd *cmd);
+
/* readline */
char * vshReadline(vshControl *ctl, const char *prompt);