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 | 351 +++++----------------------------------------------------- tools/vsh.c | 245 ++++++++++++++++++++++++++++++++++++++++ tools/vsh.h | 71 ++++++++++++ 3 files changed, 346 insertions(+), 321 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 2d198cd..391c155 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -188,20 +188,18 @@ virshReconnect(vshControl *ctl) priv->blockJobNoBytes = false; } +int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, + const char *bytes, size_t nbytes, void *opaque) +{ + int *fd = opaque; -/* - * "connect" command + return safewrite(*fd, bytes, nbytes); +} + +/* --------------- + * Command Connect + * --------------- */ -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", @@ -216,6 +214,17 @@ static const vshCmdOptDef opts_connect[] = { {.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} +}; + static bool cmdConnect(vshControl *ctl, const vshCmd *cmd) { @@ -236,18 +245,18 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd) priv->conn = NULL; } - VIR_FREE(ctl->name); + VIR_FREE(ctl->connname); if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) return false; - ctl->name = vshStrdup(ctl, name); + ctl->connname = vshStrdup(ctl, name); priv->useGetInfo = false; priv->useSnapshotOld = false; priv->blockJobNoBytes = false; priv->readonly = ro; - priv->conn = virshConnect(ctl, ctl->name, priv->readonly); + priv->conn = virshConnect(ctl, ctl->connname, priv->readonly); if (!priv->conn) { vshError(ctl, "%s", _("Failed to connect to the hypervisor")); @@ -261,281 +270,11 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd) return true; } -int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, - const char *bytes, size_t nbytes, void *opaque) -{ - int *fd = opaque; - - return safewrite(*fd, bytes, nbytes); -} - -/* --------------- - * Commands - * --------------- - */ - -/* - * "help" command - */ -static const vshCmdInfo info_help[] = { - {.name = "help", - .data = N_("print help") - }, - {.name = "desc", - .data = N_("Prints global help, command specific help, or help for a\n" - " group of related commands") - }, - {.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") - }, - {.name = "desc", - .data = N_("Change the current directory.") - }, - {.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") - }, - {.name = "desc", - .data = N_("Print the current directory.") - }, - {.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") - }, - {.name = "desc", - .data = N_("Echo back arguments, possibly with quoting.") - }, - {.name = NULL} -}; - -static const vshCmdOptDef opts_echo[] = { - {.name = "shell", - .type = VSH_OT_BOOL, - .help = N_("escape for shell use") - }, - {.name = "xml", - .type = VSH_OT_BOOL, - .help = N_("escape for XML use") - }, - {.name = "str", - .type = VSH_OT_ALIAS, - .help = "string" - }, - {.name = "hi", - .type = VSH_OT_ALIAS, - .help = "string=hello" - }, - {.name = "string", - .type = VSH_OT_ARGV, - .help = N_("arguments to echo") - }, - {.name = NULL} -}; - -/* Exists mainly for debugging virsh, but also handy for adding back - * quotes for later evaluation. - */ -static 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; -} - -/* - * "quit" command - */ -static const vshCmdInfo info_quit[] = { - {.name = "help", - .data = N_("quit this interactive terminal") - }, - {.name = "desc", - .data = "" - }, - {.name = NULL} -}; - -static bool -cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) -{ - ctl->imode = false; - return true; -} - /* --------------- * Utils for work with runtime commands data * --------------- */ - static bool virshConnectionUsability(vshControl *ctl, virConnectPtr conn) { @@ -1061,48 +800,18 @@ virshParseArgv(vshControl *ctl, int argc, char **argv) } static const vshCmdDef virshCmds[] = { - {.name = "cd", - .handler = cmdCd, - .opts = opts_cd, - .info = info_cd, - .flags = VSH_CMD_FLAG_NOCONNECT - }, + VSH_CMD_CD, + VSH_CMD_ECHO, + VSH_CMD_EXIT, + VSH_CMD_HELP, + VSH_CMD_PWD, + VSH_CMD_QUIT, {.name = "connect", .handler = cmdConnect, .opts = opts_connect, .info = info_connect, .flags = VSH_CMD_FLAG_NOCONNECT }, - {.name = "echo", - .handler = cmdEcho, - .opts = opts_echo, - .info = info_echo, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "exit", - .handler = cmdQuit, - .opts = NULL, - .info = info_quit, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "help", - .handler = cmdHelp, - .opts = opts_help, - .info = info_help, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "pwd", - .handler = cmdPwd, - .opts = NULL, - .info = info_pwd, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "quit", - .handler = cmdQuit, - .opts = NULL, - .info = info_quit, - .flags = VSH_CMD_FLAG_NOCONNECT - }, {.name = NULL} }; diff --git a/tools/vsh.c b/tools/vsh.c index 043d0fb..03ff859 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -2748,3 +2748,248 @@ vshDeinit(vshControl *ctl) vshReadlineDeinit(ctl); vshCloseLogFile(ctl); } + +/* ----------------------------------------------- + * Generic commands available to use by any client + * ----------------------------------------------- + */ +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} +}; + +const vshCmdInfo info_help[] = { + {.name = "help", + .data = N_("print help") + }, + {.name = "desc", + .data = N_("Prints global help, command specific help, or help for a\n" + " group of related commands") + }, + {.name = NULL} +}; + +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; + } +} + +const vshCmdOptDef opts_cd[] = { + {.name = "dir", + .type = VSH_OT_STRING, + .help = N_("directory to switch to (default: home or else root)") + }, + {.name = NULL} +}; + +const vshCmdInfo info_cd[] = { + {.name = "help", + .data = N_("change the current directory") + }, + {.name = "desc", + .data = N_("Change the current directory.") + }, + {.name = NULL} +}; + +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; +} + +const vshCmdOptDef opts_echo[] = { + {.name = "shell", + .type = VSH_OT_BOOL, + .help = N_("escape for shell use") + }, + {.name = "xml", + .type = VSH_OT_BOOL, + .help = N_("escape for XML use") + }, + {.name = "str", + .type = VSH_OT_ALIAS, + .help = "string" + }, + {.name = "hi", + .type = VSH_OT_ALIAS, + .help = "string=hello" + }, + {.name = "string", + .type = VSH_OT_ARGV, + .help = N_("arguments to echo") + }, + {.name = NULL} +}; + +const vshCmdInfo info_echo[] = { + {.name = "help", + .data = N_("echo arguments") + }, + {.name = "desc", + .data = N_("Echo back arguments, possibly with quoting.") + }, + {.name = NULL} +}; + +/* 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; +} + +const vshCmdInfo info_pwd[] = { + {.name = "help", + .data = N_("print the current directory") + }, + {.name = "desc", + .data = N_("Print the current directory.") + }, + {.name = NULL} +}; + +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; +} + +const vshCmdInfo info_quit[] = { + {.name = "help", + .data = N_("quit this interactive terminal") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +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..37416c7 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -363,6 +363,77 @@ int vshEventStart(vshControl *ctl, int timeout_ms); void vshEventTimeout(int timer, void *opaque); int vshEventWait(vshControl *ctl); +/* generic commands */ +extern const vshCmdOptDef opts_help[]; +extern const vshCmdInfo info_help[]; +extern const vshCmdOptDef opts_cd[]; +extern const vshCmdInfo info_cd[]; +extern const vshCmdOptDef opts_echo[]; +extern const vshCmdInfo info_echo[]; +extern const vshCmdInfo info_pwd[]; +extern const vshCmdInfo info_quit[]; + +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); + +# define VSH_CMD_CD \ + { \ + .name = "cd", \ + .handler = cmdCd, \ + .opts = opts_cd, \ + .info = info_cd, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_ECHO \ + { \ + .name = "echo", \ + .handler = cmdEcho, \ + .opts = opts_echo, \ + .info = info_echo, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_EXIT \ + { \ + .name = "exit", \ + .handler = cmdQuit, \ + .opts = NULL, \ + .info = info_quit, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_HELP \ + { \ + .name = "help", \ + .handler = cmdHelp, \ + .opts = opts_help, \ + .info = info_help, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_PWD \ + { \ + .name = "pwd", \ + .handler = cmdPwd, \ + .opts = NULL, \ + .info = info_pwd, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_QUIT \ + { \ + .name = "quit", \ + .handler = cmdQuit, \ + .opts = NULL, \ + .info = info_quit, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + + /* readline */ char * vshReadline(vshControl *ctl, const char *prompt); -- 2.4.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list