Everyone[1] seems to be doing funky bash auto-completion for commands these days, so I thought I'd make a stab at doing something for virsh. First of all I needed the command line help in an easier format to deal with, so I've added virsh _complete-command commands|arguments|options eg, $ virsh _complete-command commands help attach-device attach-disk attach-interface autostart capabilities connect console ...snip rest... $ virsh _complete-command arguments vcpupin domain vcpu cpulist $ virsh _complete-command options -c --connect -r --readonly -d --debug -h --help -q --quiet -t --timing -l --log -v --version $ virsh _complete-command options net-list --inactive --all That's enough to get all the basic commands & options auto-completing. For the arguments though I wanted to be able to auto-complete domain names, ids and UUIDs. likewise for storage pools and networks. I also wanted to auto-complete connect URIs ,eg test://default, qemu:///system $ virsh _complete-domain ISCSIServer 35e5efcc-f6a3-b489-fc33-99f2e4792e6d untangle dc502cb8-4bdd-7a15-f35a-cf76287ea2bd VirtTest 82038f2a-1344-aaf7-1a85-2a7250be2076 xenner 18c23d31-4d74-0bff-986d-e4251d2a2ff0 And $ virsh _complete-uri test:///default qemu:///system lxc:/// For this latter command, I needed to add a new API /** * virConnectURIs: * uris: pointer to a list to be filled with URI strings * * This functions returns the list of available connection * URIs. The caller is responsible for freeing the returned * strings, and the array they're stored in. * * Returns the number of URIs in case of success or -1 in * case of error. */ int virConnectURIs(char ***uris); This simply calls the existing probe() method for each driver and stores the list of URIs returned. Finally to tie this all together simply needs a little helper script which by convention is added to the /etc/bash_completion.d/. On Fedora this isn't enable by default, so you have to turn it on with source /etc/bash_completion.d/virsh To your $HOME/.bashrc Daniel [1] For a definition of 'everyone' which includes git & mercurial b/src/virsh.bash | 79 +++++++ include/libvirt/libvirt.h | 1 include/libvirt/libvirt.h.in | 1 libvirt.spec.in | 1 src/Makefile.am | 10 src/libvirt.c | 54 +++++ src/libvirt_sym.version | 2 src/test.c | 6 src/virsh.c | 442 ++++++++++++++++++++++++++++++++++++++++++- 9 files changed, 581 insertions(+), 15 deletions(-) diff -r f3413463b3ff include/libvirt/libvirt.h --- a/include/libvirt/libvirt.h Sun Jul 13 13:14:50 2008 +0100 +++ b/include/libvirt/libvirt.h Mon Jul 14 11:54:10 2008 +0100 @@ -397,6 +397,7 @@ virConnectAuthPtr auth, int flags); int virConnectClose (virConnectPtr conn); +int virConnectURIs (char ***uris); const char * virConnectGetType (virConnectPtr conn); int virConnectGetVersion (virConnectPtr conn, unsigned long *hvVer); diff -r f3413463b3ff include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in Sun Jul 13 13:14:50 2008 +0100 +++ b/include/libvirt/libvirt.h.in Mon Jul 14 11:54:10 2008 +0100 @@ -397,6 +397,7 @@ virConnectAuthPtr auth, int flags); int virConnectClose (virConnectPtr conn); +int virConnectURIs (char ***uris); const char * virConnectGetType (virConnectPtr conn); int virConnectGetVersion (virConnectPtr conn, unsigned long *hvVer); diff -r f3413463b3ff libvirt.spec.in --- a/libvirt.spec.in Sun Jul 13 13:14:50 2008 +0100 +++ b/libvirt.spec.in Mon Jul 14 11:54:10 2008 +0100 @@ -227,6 +227,7 @@ %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/ %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/networks/ %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/qemu/networks/autostart +%{_sysconfdir}/bash_completion.d/virsh %{_sysconfdir}/rc.d/init.d/libvirtd %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd %config(noreplace) %{_sysconfdir}/libvirt/libvirtd.conf diff -r f3413463b3ff src/Makefile.am --- a/src/Makefile.am Sun Jul 13 13:14:50 2008 +0100 +++ b/src/Makefile.am Mon Jul 14 11:54:10 2008 +0100 @@ -25,8 +25,9 @@ confdir = $(sysconfdir)/libvirt/ conf_DATA = qemu.conf +bashdir = $(sysconfdir)/bash_completion.d -EXTRA_DIST = libvirt_sym.version $(conf_DATA) +EXTRA_DIST = libvirt_sym.version $(conf_DATA) virsh.bash lib_LTLIBRARIES = libvirt.la @@ -153,7 +154,12 @@ endif # Create the /var/cache/libvirt directory when installing. -install-exec-local: +install-exec-local:: $(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/libvirt + $(MKDIR_P) $(DESTDIR)$(bashdir) + $(INSTALL_SCRIPT) virsh.bash $(bashdir)/virsh + +uninstall-local:: + $(RM) -f $(bashdir)/virsh CLEANFILES = *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda diff -r f3413463b3ff src/libvirt.c --- a/src/libvirt.c Sun Jul 13 13:14:50 2008 +0100 +++ b/src/libvirt.c Mon Jul 14 11:54:10 2008 +0100 @@ -37,6 +37,7 @@ #include "uuid.h" #include "util.h" #include "test.h" +#include "memory.h" #include "xen_unified.h" #include "remote_internal.h" #include "qemu_driver.h" @@ -701,7 +702,8 @@ int probes = 0; for (i = 0; i < virDriverTabCount; i++) { if ((virDriverTab[i]->probe != NULL) && - ((latest = virDriverTab[i]->probe()) != NULL)) { + ((latest = virDriverTab[i]->probe()) != NULL) && + STRNEQ(latest, "test:///default")) { probes++; DEBUG("Probed %s", latest); @@ -946,6 +948,56 @@ if (virUnrefConnect(conn) < 0) return (-1); return (0); +} + + +/** + * virConnectURIs: + * uris: pointer to a list to be filled with URI strings + * + * This functions returns the list of available connection + * URIs. The caller is responsible for freeing the returned + * strings, and the array they're stored in. + * + * Returns the number of URIs in case of success or -1 in + * case of error. + */ +int +virConnectURIs(char ***uris) +{ + const char *uri; + char **ret = NULL; + int nuris = 0, i; + DEBUG("uris=%p", uris); + + if (!initialized) + if (virInitialize() < 0) + return -1; + + if (!uris) + return -1; + + for (i = 0; i < virDriverTabCount; i++) { + if ((virDriverTab[i]->probe != NULL) && + ((uri = virDriverTab[i]->probe()) != NULL)) { + if (VIR_REALLOC_N(ret, nuris + 1) < 0) + goto no_memory; + if ((ret[nuris++] = strdup(uri)) == NULL) + goto no_memory; + + DEBUG("Probed %s", uri); + } + } + + *uris = ret; + return (nuris); + +no_memory: + for (i = 0 ; i < nuris ; i++) + VIR_FREE(ret[i]); + VIR_FREE(ret); + virLibConnError(NULL, VIR_ERR_NO_MEMORY, NULL); + return -1; } /* Not for public use. This function is part of the internal diff -r f3413463b3ff src/libvirt_sym.version --- a/src/libvirt_sym.version Sun Jul 13 13:14:50 2008 +0100 +++ b/src/libvirt_sym.version Mon Jul 14 11:54:10 2008 +0100 @@ -5,7 +5,7 @@ virConnectOpenReadOnly; virConnectOpenAuth; virConnectAuthPtrDefault; - + virConnectURIs; virConnectClose; virConnectGetType; virConnectGetVersion; diff -r f3413463b3ff src/test.c --- a/src/test.c Sun Jul 13 13:14:50 2008 +0100 +++ b/src/test.c Mon Jul 14 11:54:10 2008 +0100 @@ -215,6 +215,10 @@ " </ip>" "</network>"; + +static const char *testProbe(void) { + return "test:///default"; +} static int testOpenDefault(virConnectPtr conn) { int u; @@ -1545,7 +1549,7 @@ VIR_DRV_TEST, "Test", LIBVIR_VERSION_NUMBER, - NULL, /* probe */ + testProbe, /* probe */ testOpen, /* open */ testClose, /* close */ NULL, /* supports_feature */ diff -r f3413463b3ff src/virsh.bash --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/virsh.bash Mon Jul 14 11:54:10 2008 +0100 @@ -0,0 +1,79 @@ + +_virsh() +{ + local cur prev cmd cmd_index arg arg_index i results + local virsh="$1" + local canonical=0 + + COMPREPLY=() + cur="$2" + prev="$3" + + if [[ $COMP_CWORD > 1 && ${COMP_WORDS[COMP_CWORD-1]} = '--connect' ]]; then + results="$("$virsh" _complete-uri)" || nets="" + fi + + if [ -z "$results" ]; then + # searching for the command name + for ((i=1; $i<=$COMP_CWORD; i++)); do + if [[ ${COMP_WORDS[i]} != -* ]]; then + if [[ ${COMP_WORDS[i-1]} != '--connect' ]]; then + if [[ $i < $COMP_CWORD ]]; then + cmd="${COMP_WORDS[i]}" + cmd_index=$i + arg_index=`expr $COMP_CWORD - $cmd_index` + break + fi + fi + fi + done + + if [[ "$cur" == -* ]]; then + # Generate args - global or command specific + results="$("$virsh" _complete-command options "$cmd")" + else + if [ -z "$cmd" ]; then + # No command set, so generate list of all commands + results="$("$virsh" _complete-command commands)" || commands="" + else + # Command set, to generate command specific args + n=0 + for i in "$("$virsh" _complete-command arguments "$cmd")" + do + n=`expr $n + 1` + if [ $n = $arg_index ]; then + arg=$i + break + fi + done + + case $arg in + file) + COMPREPLY=(${COMPREPLY[@]:-} $(compgen -o "default" -- "$cur")) + ;; + + domain) + results="$("$virsh" _complete-domain)" || doms="" + ;; + + network) + results="$("$virsh" _complete-network)" || nets="" + ;; + + pool) + results="$("$virsh" _complete-pool)" || nets="" + ;; + + uri) + results="$("$virsh" _complete-uri)" || nets="" + ;; + esac + fi + fi + fi + if [ ! -z "$results" ]; then + COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$results' -- "$cur")) + fi +} + +complete -F _virsh virsh diff -r f3413463b3ff src/virsh.c --- a/src/virsh.c Sun Jul 13 13:14:50 2008 +0100 +++ b/src/virsh.c Mon Jul 14 11:54:10 2008 +0100 @@ -329,6 +329,409 @@ * --------------- */ + + +/* + * "_compcmds" command + */ +static vshCmdInfo info_compcommand[] = { + {"syntax", "_complete-command [commands|arguments|options] [<command>]"}, + {"help", gettext_noop("print shell completion data")}, + {"desc", gettext_noop("Prints data useful for shell autocompletion of commands.")}, + + {NULL, NULL} +}; + +static vshCmdOptDef opts_compcommand[] = { + {"type", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("type of data")}, + {"cmd", VSH_OT_DATA, 0, gettext_noop("command name")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdCompCommand(vshControl * ctl, vshCmd * cmd) +{ + const char *type = vshCommandOptString(cmd, "type", NULL); + const char *name = vshCommandOptString(cmd, "cmd", NULL); + + if (STREQ(type, "commands")) { + vshCmdDef *def; + for (def = commands; def->name; def++) + if (def->name[0] != '_') + vshPrint(ctl, "%s\n", def->name); + } else if (STREQ(type, "options")) { + if (name) { + vshCmdDef *def = vshCmddefSearch(name); + if (!def) { + vshError(ctl, FALSE, _("command '%s' doesn't exist"), name); + return FALSE; + } else if (def->opts) { + vshCmdOptDef *opt; + for (opt = def->opts; opt->name; opt++) + if (opt->type == VSH_OT_BOOL || + opt->type == VSH_OT_INT || + opt->type == VSH_OT_STRING) + vshPrint(ctl, "--%s\n", opt->name); + } + } else { + vshPrint(ctl, + "-c\n--connect\n" + "-r\n--readonly\n" + "-d\n--debug\n" + "-h\n--help\n" + "-q\n--quiet\n" + "-t\n--timing\n" + "-l\n--log\n" + "-v\n--version\n"); + } + } else if (STREQ(type, "arguments")) { + if (!name) { + vshError(ctl, FALSE, "%s", _("no command specified")); + return FALSE; + } else { + vshCmdDef *def = vshCmddefSearch(name); + if (!def) { + vshError(ctl, FALSE, _("command '%s' doesn't exist"), name); + return FALSE; + } else if (def->opts) { + vshCmdOptDef *opt; + for (opt = def->opts; opt->name; opt++) + if (opt->type == VSH_OT_DATA) + vshPrint(ctl, "%s\n", opt->name); + } + } + } + return TRUE; +} + +/* + * "_compuris" command + */ +static vshCmdInfo info_compuri[] = { + {"syntax", "_complete-uri"}, + {"help", gettext_noop("print shell completion data for URIs")}, + {"desc", gettext_noop("Prints data useful for shell autocompletion of URIs.")}, + + {NULL, NULL} +}; + +static vshCmdOptDef opts_compuri[] = { + {NULL, 0, 0, NULL} +}; + +static int +cmdCompUri(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + char **uris = NULL; + int nuris, i; + + nuris = virConnectURIs(&uris); + if (nuris < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list connection URIs")); + return FALSE; + } + for (i = 0 ; i < nuris ; i++) { + vshPrint(ctl, "%s\n", uris[i]); + free(uris[i]); + } + + free(uris); + return TRUE; +} +/* + * "_compdomains" command + */ +static vshCmdInfo info_compdomain[] = { + {"syntax", "_complete-domain"}, + {"help", gettext_noop("print shell completion data for domains")}, + {"desc", gettext_noop("Prints data useful for shell autocompletion of domains.")}, + + {NULL, NULL} +}; + +static vshCmdOptDef opts_compdomain[] = { + {NULL, 0, 0, NULL} +}; + +static int +cmdCompDomain(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + int *ids = NULL; + char **names = NULL; + int maxname = 0, maxid = 0, i; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + maxid = virConnectNumOfDomains(ctl->conn); + if (maxid < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active domains")); + return FALSE; + } + if (maxid) { + ids = vshMalloc(ctl, sizeof(int) * maxid); + + if ((maxid = virConnectListDomains(ctl->conn, &ids[0], maxid)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active domains")); + free(ids); + return FALSE; + } + + qsort(&ids[0], maxid, sizeof(int), idsorter); + } + maxname = virConnectNumOfDefinedDomains(ctl->conn); + if (maxname < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive domains")); + free(ids); + return FALSE; + } + if (maxname) { + names = vshMalloc(ctl, sizeof(char *) * maxname); + + if ((maxname = virConnectListDefinedDomains(ctl->conn, names, maxname)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive domains")); + free(ids); + free(names); + return FALSE; + } + + qsort(&names[0], maxname, sizeof(char*), namesorter); + } + + for (i = 0; i < maxid; i++) { + char uuid[VIR_UUID_STRING_BUFLEN]; + virDomainPtr dom = virDomainLookupByID(ctl->conn, ids[i]); + /* this kind of work with domains is not atomic operation */ + if (!dom) + continue; + virDomainGetUUIDString(dom, uuid); + vshPrint(ctl, "%d\n%s\n%s\n", + virDomainGetID(dom), + virDomainGetName(dom), + uuid); + virDomainFree(dom); + } + for (i = 0; i < maxname; i++) { + char uuid[VIR_UUID_STRING_BUFLEN]; + virDomainPtr dom = virDomainLookupByName(ctl->conn, names[i]); + + /* this kind of work with domains is not atomic operation */ + if (!dom) { + free(names[i]); + continue; + } + virDomainGetUUIDString(dom, uuid); + vshPrint(ctl, "%s\n%s\n", + virDomainGetName(dom), + uuid); + virDomainFree(dom); + free(names[i]); + } + free(ids); + free(names); + return TRUE; +} + +/* + * "_compnetworks" command + */ +static vshCmdInfo info_compnetwork[] = { + {"syntax", "_complete-network"}, + {"help", gettext_noop("print shell completion data for networks")}, + {"desc", gettext_noop("Prints data useful for shell autocompletion of networks.")}, + + {NULL, NULL} +}; + +static vshCmdOptDef opts_compnetwork[] = { + {NULL, 0, 0, NULL} +}; + +static int +cmdCompNetwork(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + char **activeNames = NULL, **inactiveNames = NULL; + int maxactive = 0, maxinactive = 0, i; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + maxactive = virConnectNumOfNetworks(ctl->conn); + if (maxactive < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active networks")); + return FALSE; + } + if (maxactive) { + activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); + + if ((maxactive = virConnectListNetworks(ctl->conn, activeNames, + maxactive)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active networks")); + free(activeNames); + return FALSE; + } + + qsort(&activeNames[0], maxactive, sizeof(char *), namesorter); + } + maxinactive = virConnectNumOfDefinedNetworks(ctl->conn); + if (maxinactive < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive networks")); + free(activeNames); + return FALSE; + } + if (maxinactive) { + inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive); + + if ((maxinactive = virConnectListDefinedNetworks(ctl->conn, inactiveNames, maxinactive)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive networks")); + free(activeNames); + free(inactiveNames); + return FALSE; + } + + qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter); + } + + for (i = 0; i < maxactive; i++) { + char uuid[VIR_UUID_STRING_BUFLEN]; + virNetworkPtr network = virNetworkLookupByName(ctl->conn, activeNames[i]); + + /* this kind of work with networks is not atomic operation */ + if (!network) { + free(activeNames[i]); + continue; + } + virNetworkGetUUIDString(network, uuid); + vshPrint(ctl, "%s\n%s\n", + virNetworkGetName(network), + uuid); + virNetworkFree(network); + free(activeNames[i]); + } + for (i = 0; i < maxinactive; i++) { + char uuid[VIR_UUID_STRING_BUFLEN]; + virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]); + + /* this kind of work with networks is not atomic operation */ + if (!network) { + free(inactiveNames[i]); + continue; + } + virNetworkGetUUIDString(network, uuid); + vshPrint(ctl, "%s\n%s\n", + virNetworkGetName(network), + uuid); + + virNetworkFree(network); + free(inactiveNames[i]); + } + free(activeNames); + free(inactiveNames); + + return TRUE; +} + + +/* + * "_comppools" command + */ +static vshCmdInfo info_comppool[] = { + {"syntax", "_complete-pool"}, + {"help", gettext_noop("print shell completion data for pools")}, + {"desc", gettext_noop("Prints data useful for shell autocompletion of pools.")}, + + {NULL, NULL} +}; + +static vshCmdOptDef opts_comppool[] = { + {NULL, 0, 0, NULL} +}; + +static int +cmdCompPool(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + char **activeNames = NULL, **inactiveNames = NULL; + int maxactive = 0, maxinactive = 0, i; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + maxactive = virConnectNumOfStoragePools(ctl->conn); + if (maxactive < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active pools")); + return FALSE; + } + if (maxactive) { + activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); + + if ((maxactive = virConnectListStoragePools(ctl->conn, activeNames, + maxactive)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list active pools")); + free(activeNames); + return FALSE; + } + + qsort(&activeNames[0], maxactive, sizeof(char *), namesorter); + } + maxinactive = virConnectNumOfDefinedStoragePools(ctl->conn); + if (maxinactive < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive pools")); + free(activeNames); + return FALSE; + } + if (maxinactive) { + inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive); + + if ((maxinactive = virConnectListDefinedStoragePools(ctl->conn, inactiveNames, maxinactive)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list inactive pools")); + free(activeNames); + free(inactiveNames); + return FALSE; + } + + qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter); + } + + for (i = 0; i < maxactive; i++) { + char uuid[VIR_UUID_STRING_BUFLEN]; + virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn, activeNames[i]); + + /* this kind of work with pools is not atomic operation */ + if (!pool) { + free(activeNames[i]); + continue; + } + virStoragePoolGetUUIDString(pool, uuid); + vshPrint(ctl, "%s\n%s\n", + virStoragePoolGetName(pool), + uuid); + virStoragePoolFree(pool); + free(activeNames[i]); + } + for (i = 0; i < maxinactive; i++) { + char uuid[VIR_UUID_STRING_BUFLEN]; + virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn, inactiveNames[i]); + + /* this kind of work with pools is not atomic operation */ + if (!pool) { + free(inactiveNames[i]); + continue; + } + virStoragePoolGetUUIDString(pool, uuid); + vshPrint(ctl, "%s\n%s\n", + virStoragePoolGetName(pool), + uuid); + + virStoragePoolFree(pool); + free(inactiveNames[i]); + } + free(activeNames); + free(inactiveNames); + + return TRUE; +} + + /* * "help" command */ @@ -355,8 +758,9 @@ vshPrint(ctl, "%s", _("Commands:\n\n")); for (def = commands; def->name; def++) - vshPrint(ctl, " %-15s %s\n", def->name, - N_(vshCmddefGetInfo(def, "help"))); + if (def->name[0] != '_') + vshPrint(ctl, " %-15s %s\n", def->name, + N_(vshCmddefGetInfo(def, "help"))); return TRUE; } return vshCmddefHelp(ctl, cmdname, FALSE); @@ -426,7 +830,7 @@ }; static vshCmdOptDef opts_connect[] = { - {"name", VSH_OT_DATA, 0, gettext_noop("hypervisor connection URI")}, + {"uri", VSH_OT_DATA, 0, gettext_noop("hypervisor connection URI")}, {"readonly", VSH_OT_BOOL, 0, gettext_noop("read-only connection")}, {NULL, 0, 0, NULL} }; @@ -446,7 +850,7 @@ } free(ctl->name); - ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "name", NULL)); + ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "uri", NULL)); if (!ro) { ctl->conn = virConnectOpen(ctl->name); @@ -1004,7 +1408,7 @@ }; static vshCmdOptDef opts_start[] = { - {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")}, + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")}, {NULL, 0, 0, NULL} }; @@ -1017,7 +1421,7 @@ if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; - if (!(dom = vshCommandOptDomainBy(ctl, cmd, "name", NULL, VSH_BYNAME))) + if (!(dom = vshCommandOptDomainBy(ctl, cmd, "domain", NULL, VSH_BYNAME))) return FALSE; if (virDomainGetID(dom) != (unsigned int)-1) { @@ -2650,7 +3054,7 @@ }; static vshCmdOptDef opts_network_start[] = { - {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive network")}, + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive network")}, {NULL, 0, 0, NULL} }; @@ -2663,7 +3067,7 @@ if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; - if (!(network = vshCommandOptNetworkBy(ctl, cmd, "name", NULL, VSH_BYNAME))) + if (!(network = vshCommandOptNetworkBy(ctl, cmd, "network", NULL, VSH_BYNAME))) return FALSE; if (virNetworkCreate(network) == 0) { @@ -3554,7 +3958,7 @@ }; static vshCmdOptDef opts_pool_start[] = { - {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive pool")}, + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive pool")}, {NULL, 0, 0, NULL} }; @@ -3567,7 +3971,7 @@ if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; - if (!(pool = vshCommandOptPoolBy(ctl, cmd, "name", NULL, VSH_BYNAME))) + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME))) return FALSE; if (virStoragePoolCreate(pool, 0) == 0) { @@ -5074,6 +5478,12 @@ * Commands */ static vshCmdDef commands[] = { + {"_complete-command", cmdCompCommand, opts_compcommand, info_compcommand}, + {"_complete-uri", cmdCompUri, opts_compuri, info_compuri}, + {"_complete-domain", cmdCompDomain, opts_compdomain, info_compdomain}, + {"_complete-network", cmdCompNetwork, opts_compnetwork, info_compnetwork}, + {"_complete-pool", cmdCompPool, opts_comppool, info_comppool}, + {"help", cmdHelp, opts_help, info_help}, {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, {"attach-disk", cmdAttachDisk, opts_attach_disk, info_attach_disk}, @@ -6514,6 +6924,18 @@ ret = vshCommandParse(ctl, cmdstr); free(cmdstr); + + /* Special case 'help' to avoid virConnectOpen */ + if (ctl->cmd && + ctl->cmd->def && + ctl->cmd->def->name && + (STREQ(ctl->cmd->def->name, "help") || + STREQ(ctl->cmd->def->name, "_complete-command") || + STREQ(ctl->cmd->def->name, "_complete-uri"))) { + ret = vshCommandRun(ctl, ctl->cmd); + exit(ret ? EXIT_SUCCESS : EXIT_FAILURE); + } + return ret; } return TRUE; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list