The very opposite action to removing client specific data from vsh. As all the generic methods/structures/variables have been moved to vsh, there's no need for them in virsh. --- tools/virsh.c | 2393 ++++----------------------------------------------------- tools/virsh.h | 428 ----------- tools/vsh.c | 2 +- 3 files changed, 140 insertions(+), 2683 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index f293d0f..9280b40 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -25,7 +25,6 @@ #include <config.h> #include "virsh.h" -#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -41,7 +40,6 @@ #include <limits.h> #include <sys/stat.h> #include <inttypes.h> -#include <strings.h> #include <signal.h> #if WITH_READLINE @@ -85,59 +83,6 @@ static char *progname; static const vshCmdGrp cmdGroups[]; -/* Bypass header poison */ -#undef strdup - -void * -_vshMalloc(vshControl *ctl, size_t size, const char *filename, int line) -{ - char *x; - - if (VIR_ALLOC_N(x, size) == 0) - return x; - vshError(ctl, _("%s: %d: failed to allocate %d bytes"), - filename, line, (int) size); - exit(EXIT_FAILURE); -} - -void * -_vshCalloc(vshControl *ctl, size_t nmemb, size_t size, const char *filename, - int line) -{ - char *x; - - if (!xalloc_oversized(nmemb, size) && - VIR_ALLOC_N(x, nmemb * size) == 0) - return x; - vshError(ctl, _("%s: %d: failed to allocate %d bytes"), - filename, line, (int) (size*nmemb)); - exit(EXIT_FAILURE); -} - -char * -_vshStrdup(vshControl *ctl, const char *s, const char *filename, int line) -{ - char *x; - - if (VIR_STRDUP(x, s) >= 0) - return x; - vshError(ctl, _("%s: %d: failed to allocate %lu bytes"), - filename, line, (unsigned long)strlen(s)); - exit(EXIT_FAILURE); -} - -/* Poison the raw allocating identifiers in favor of our vsh variants. */ -#define strdup use_vshStrdup_instead_of_strdup - -int -vshNameSorter(const void *a, const void *b) -{ - const char **sa = (const char**)a; - const char **sb = (const char**)b; - - return vshStrcasecmp(*sa, *sb); -} - double virshPrettyCapacity(unsigned long long val, const char **unit) { @@ -913,25 +858,10 @@ cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } /* --------------- - * Utils for work with command definition + * Utils for work with runtime commands data * --------------- */ -const char * -vshCmddefGetInfo(const vshCmdDef * cmd, const char *name) -{ - const vshCmdInfo *info; - - for (info = cmd->info; info && info->name; info++) { - if (STREQ(info->name, name)) - return info->data; - } - return NULL; -} -/* Validate that the options associated with cmd can be parsed. */ -static int -vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg, - uint32_t *opts_required) /* * virshCommandOptTimeoutToMs: * @ctl virsh control structure @@ -945,2253 +875,211 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg, int virshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout) { - size_t i; - bool optional = false; - - *opts_need_arg = 0; - *opts_required = 0; - - if (!cmd->opts) - return 0; - - for (i = 0; cmd->opts[i].name; i++) { - const vshCmdOptDef *opt = &cmd->opts[i]; - - if (i > 31) - return -1; /* too many options */ - if (opt->type == VSH_OT_BOOL) { - optional = true; - if (opt->flags & VSH_OFLAG_REQ) - return -1; /* bool options can't be mandatory */ - continue; - } - if (opt->type == VSH_OT_ALIAS) { - size_t j; - char *name = (char *)opt->help; /* cast away const */ - char *p; - - if (opt->flags || !opt->help) - return -1; /* alias options are tracked by the original name */ - if ((p = strchr(name, '=')) && - VIR_STRNDUP(name, name, p - name) < 0) - return -1; - for (j = i + 1; cmd->opts[j].name; j++) { - if (STREQ(name, cmd->opts[j].name) && - cmd->opts[j].type != VSH_OT_ALIAS) - break; - } - if (name != opt->help) { - VIR_FREE(name); - /* If alias comes with value, replacement must not be bool */ - if (cmd->opts[j].type == VSH_OT_BOOL) - return -1; - } - if (!cmd->opts[j].name) - return -1; /* alias option must map to a later option name */ - continue; - } - if (opt->flags & VSH_OFLAG_REQ_OPT) { - if (opt->flags & VSH_OFLAG_REQ) - *opts_required |= 1 << i; - else - optional = true; - continue; - } + int ret; + unsigned int utimeout; - *opts_need_arg |= 1 << i; - if (opt->flags & VSH_OFLAG_REQ) { - if (optional && opt->type != VSH_OT_ARGV) - return -1; /* mandatory options must be listed first */ - *opts_required |= 1 << i; - } else { - optional = true; - } + if ((ret = vshCommandOptUInt(ctl, cmd, "timeout", &utimeout)) <= 0) + return ret; - if (opt->type == VSH_OT_ARGV && cmd->opts[i + 1].name) - return -1; /* argv option must be listed last */ + /* Ensure that the timeout is not zero and that we can convert + * it from seconds to milliseconds without overflowing. */ + if (utimeout == 0 || utimeout > INT_MAX / 1000) { + vshError(ctl, + _("Numeric value '%u' for <%s> option is malformed or out of range"), + utimeout, + "timeout"); + ret = -1; + } else { + *timeout = ((int) utimeout) * 1000; } - return 0; + + return ret; } -static vshCmdOptDef helpopt = { - .name = "help", - .type = VSH_OT_BOOL, - .help = N_("print help for this function") -}; -static const vshCmdOptDef * -vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, - uint32_t *opts_seen, int *opt_index, char **optstr) static bool virshConnectionUsability(vshControl *ctl, virConnectPtr conn) { - size_t i; - const vshCmdOptDef *ret = NULL; - char *alias = NULL; - - if (STREQ(name, helpopt.name)) - return &helpopt; - - for (i = 0; cmd->opts && cmd->opts[i].name; i++) { - const vshCmdOptDef *opt = &cmd->opts[i]; - - if (STREQ(opt->name, name)) { - if (opt->type == VSH_OT_ALIAS) { - char *value; - - /* Two types of replacements: - opt->help = "string": straight replacement of name - opt->help = "string=value": treat boolean flag as - alias of option and its default value */ - sa_assert(!alias); - if (VIR_STRDUP(alias, opt->help) < 0) - goto cleanup; - name = alias; - if ((value = strchr(name, '='))) { - *value = '\0'; - if (*optstr) { - vshError(ctl, _("invalid '=' after option --%s"), - opt->name); - goto cleanup; - } - if (VIR_STRDUP(*optstr, value + 1) < 0) - goto cleanup; - } - continue; - } - if ((*opts_seen & (1 << i)) && opt->type != VSH_OT_ARGV) { - vshError(ctl, _("option --%s already seen"), name); - goto cleanup; - } - *opts_seen |= 1 << i; - *opt_index = i; - ret = opt; - goto cleanup; - } + if (!conn || + virConnectIsAlive(conn) == 0) { + vshError(ctl, "%s", _("no valid connection")); + return false; } - if (STRNEQ(cmd->name, "help")) { - vshError(ctl, _("command '%s' doesn't support option --%s"), - cmd->name, name); - } - cleanup: - VIR_FREE(alias); - return ret; + /* The connection is considered dead only if + * virConnectIsAlive() successfuly says so. + */ + vshResetLibvirtError(); + + return true; } -static const vshCmdOptDef * -vshCmddefGetData(const vshCmdDef *cmd, uint32_t *opts_need_arg, - uint32_t *opts_seen) { - size_t i; - const vshCmdOptDef *opt; - if (!*opts_need_arg) - return NULL; - /* Grab least-significant set bit */ - i = ffs(*opts_need_arg) - 1; - opt = &cmd->opts[i]; - if (opt->type != VSH_OT_ARGV) - *opts_need_arg &= ~(1 << i); - *opts_seen |= 1 << i; - return opt; } -/* - * Checks for required options +/* --------------- + * Misc utils + * --------------- */ -static int -vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required, - uint32_t opts_seen) int virshDomainState(vshControl *ctl, virDomainPtr dom, int *reason) { - const vshCmdDef *def = cmd->def; - size_t i; - - opts_required &= ~opts_seen; - if (!opts_required) - return 0; + virDomainInfo info; + virshControlPtr priv = ctl->privData; - for (i = 0; def->opts[i].name; i++) { - if (opts_required & (1 << i)) { - const vshCmdOptDef *opt = &def->opts[i]; + if (reason) + *reason = -1; - vshError(ctl, - opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ? - _("command '%s' requires <%s> option") : - _("command '%s' requires --%s option"), - def->name, opt->name); + if (!priv->useGetInfo) { + int state; + if (virDomainGetState(dom, &state, reason, 0) < 0) { + virErrorPtr err = virGetLastError(); + if (err && err->code == VIR_ERR_NO_SUPPORT) + priv->useGetInfo = true; + else + return -1; + } else { + return state; } } - return -1; + + /* fall back to virDomainGetInfo if virDomainGetState is not supported */ + if (virDomainGetInfo(dom, &info) < 0) + return -1; + else + return info.state; } -const vshCmdDef * -vshCmddefSearch(const char *cmdname) /* * Initialize connection. */ static bool virshInit(vshControl *ctl) { - const vshCmdGrp *g; - const vshCmdDef *c; - - for (g = cmdGroups; g->name; g++) { - for (c = g->commands; c->name; c++) { - if (STREQ(c->name, cmdname)) - return c; - } - } + virshControlPtr priv = ctl->privData; - return NULL; -} + /* Since we have the commandline arguments parsed, we need to + * re-initialize all the debugging to make it work properly */ + vshInitDebug(ctl); -const vshCmdGrp * -vshCmdGrpSearch(const char *grpname) -{ - const vshCmdGrp *g; + if (priv->conn) + return false; - for (g = cmdGroups; g->name; g++) { - if (STREQ(g->name, grpname) || STREQ(g->keyword, grpname)) - return g; - } + /* set up the library error handler */ + virSetErrorFunc(NULL, vshErrorHandler); - return NULL; -} + if (virEventRegisterDefaultImpl() < 0) + return false; -bool -vshCmdGrpHelp(vshControl *ctl, const char *grpname) -{ - const vshCmdGrp *grp = vshCmdGrpSearch(grpname); - const vshCmdDef *cmd = NULL; + if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0) + return false; + ctl->eventLoopStarted = true; - if (!grp) { - vshError(ctl, _("command group '%s' doesn't exist"), grpname); + if ((ctl->eventTimerId = virEventAddTimeout(-1, vshEventTimeout, ctl, + NULL)) < 0) return false; - } else { - vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name, - grp->keyword); - for (cmd = grp->commands; cmd->name; cmd++) { - if (cmd->flags & VSH_CMD_FLAG_ALIAS) - continue; - vshPrint(ctl, " %-30s %s\n", cmd->name, - _(vshCmddefGetInfo(cmd, "help"))); + if (ctl->name) { + virshReconnect(ctl); + /* 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). + */ + if (!priv->conn) { + vshReportError(ctl); + return false; } } return true; } -bool -vshCmddefHelp(vshControl *ctl, const char *cmdname) static void virshDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) { - const vshCmdDef *def = vshCmddefSearch(cmdname); - - if (!def) { - vshError(ctl, _("command '%s' doesn't exist"), cmdname); - return false; - } else { - /* Don't translate desc if it is "". */ - const char *desc = vshCmddefGetInfo(def, "desc"); - const char *help = _(vshCmddefGetInfo(def, "help")); - char buf[256]; - uint32_t opts_need_arg; - uint32_t opts_required; - bool shortopt = false; /* true if 'arg' works instead of '--opt arg' */ - - if (vshCmddefOptParse(def, &opts_need_arg, &opts_required)) { - vshError(ctl, _("internal error: bad options in command: '%s'"), - def->name); - return false; - } - - fputs(_(" NAME\n"), stdout); - fprintf(stdout, " %s - %s\n", def->name, help); - - fputs(_("\n SYNOPSIS\n"), stdout); - fprintf(stdout, " %s", def->name); - if (def->opts) { - const vshCmdOptDef *opt; - for (opt = def->opts; opt->name; opt++) { - const char *fmt = "%s"; - switch (opt->type) { - case VSH_OT_BOOL: - fmt = "[--%s]"; - break; - case VSH_OT_INT: - /* xgettext:c-format */ - fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>" - : _("[--%s <number>]")); - if (!(opt->flags & VSH_OFLAG_REQ_OPT)) - shortopt = true; - break; - case VSH_OT_STRING: - /* xgettext:c-format */ - fmt = _("[--%s <string>]"); - if (!(opt->flags & VSH_OFLAG_REQ_OPT)) - shortopt = true; - break; - case VSH_OT_DATA: - fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>" : "[<%s>]"); - if (!(opt->flags & VSH_OFLAG_REQ_OPT)) - shortopt = true; - break; - case VSH_OT_ARGV: - /* xgettext:c-format */ - if (shortopt) { - fmt = (opt->flags & VSH_OFLAG_REQ) - ? _("{[--%s] <string>}...") - : _("[[--%s] <string>]..."); - } else { - fmt = (opt->flags & VSH_OFLAG_REQ) ? _("<%s>...") - : _("[<%s>]..."); - } - break; - case VSH_OT_ALIAS: - /* aliases are intentionally undocumented */ - continue; - } - fputc(' ', stdout); - fprintf(stdout, fmt, opt->name); - } - } - fputc('\n', stdout); - - if (desc[0]) { - /* Print the description only if it's not empty. */ - fputs(_("\n DESCRIPTION\n"), stdout); - fprintf(stdout, " %s\n", _(desc)); - } - - if (def->opts && def->opts->name) { - const vshCmdOptDef *opt; - fputs(_("\n OPTIONS\n"), stdout); - for (opt = def->opts; opt->name; opt++) { - switch (opt->type) { - case VSH_OT_BOOL: - snprintf(buf, sizeof(buf), "--%s", opt->name); - break; - case VSH_OT_INT: - snprintf(buf, sizeof(buf), - (opt->flags & VSH_OFLAG_REQ) ? _("[--%s] <number>") - : _("--%s <number>"), opt->name); - break; - case VSH_OT_STRING: - /* OT_STRING should never be VSH_OFLAG_REQ */ - if (opt->flags & VSH_OFLAG_REQ) { - vshError(ctl, - _("internal error: bad options in command: '%s'"), - def->name); - return false; - } - snprintf(buf, sizeof(buf), _("--%s <string>"), opt->name); - break; - case VSH_OT_DATA: - /* OT_DATA should always be VSH_OFLAG_REQ */ - if (!(opt->flags & VSH_OFLAG_REQ)) { - vshError(ctl, - _("internal error: bad options in command: '%s'"), - def->name); - return false; - } - snprintf(buf, sizeof(buf), _("[--%s] <string>"), - opt->name); - break; - case VSH_OT_ARGV: - snprintf(buf, sizeof(buf), - shortopt ? _("[--%s] <string>") : _("<%s>"), - opt->name); - break; - case VSH_OT_ALIAS: - continue; - } - - fprintf(stdout, " %-15s %s\n", buf, _(opt->help)); - } - } - fputc('\n', stdout); - } - return true; + /* nothing to be done here */ } -/* --------------- - * Utils for work with runtime commands data - * --------------- +/* + * Deinitialize virsh */ -static void -vshCommandOptFree(vshCmdOpt * arg) static bool virshDeinit(vshControl *ctl) { - vshCmdOpt *a = arg; + virshControlPtr priv = ctl->privData; - while (a) { - vshCmdOpt *tmp = a; + vshReadlineDeinit(ctl); + vshCloseLogFile(ctl); + VIR_FREE(ctl->name); + 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")); + } + virResetLastError(); - a = a->next; + if (ctl->eventLoopStarted) { + int timer; - VIR_FREE(tmp->data); - VIR_FREE(tmp); - } -} + virMutexLock(&ctl->lock); + ctl->quit = true; + /* HACK: Add a dummy timeout to break event loop */ + timer = virEventAddTimeout(0, virshDeinitTimer, NULL, NULL); + virMutexUnlock(&ctl->lock); -static void -vshCommandFree(vshCmd *cmd) -{ - vshCmd *c = cmd; + virThreadJoin(&ctl->eventLoop); - while (c) { - vshCmd *tmp = c; + if (timer != -1) + virEventRemoveTimeout(timer); - c = c->next; + if (ctl->eventTimerId != -1) + virEventRemoveTimeout(ctl->eventTimerId); - if (tmp->opts) - vshCommandOptFree(tmp->opts); - VIR_FREE(tmp); + ctl->eventLoopStarted = false; } -} -/** - * vshCommandOpt: - * @cmd: parsed command line to search - * @name: option name to search for - * @opt: result of the search - * @needData: true if option must be non-boolean - * - * Look up an option passed to CMD by NAME. Returns 1 with *OPT set - * to the option if found, 0 with *OPT set to NULL if the name is - * valid and the option is not required, -1 with *OPT set to NULL if - * the option is required but not present, and assert if NAME is not - * valid (which indicates a programming error). No error messages are - * issued if a value is returned. - */ -static int -vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt, - bool needData) -{ - vshCmdOpt *candidate = cmd->opts; - const vshCmdOptDef *valid = cmd->def->opts; - int ret = 0; - - /* See if option is valid and/or required. */ - *opt = NULL; - while (valid) { - assert(valid->name); - if (STREQ(name, valid->name)) - break; - valid++; - } - assert(!needData || valid->type != VSH_OT_BOOL); - if (valid->flags & VSH_OFLAG_REQ) - ret = -1; + virMutexDestroy(&ctl->lock); - /* See if option is present on command line. */ - while (candidate) { - if (STREQ(candidate->def->name, name)) { - *opt = candidate; - ret = 1; - break; - } - candidate = candidate->next; - } - return ret; + return true; } -/** - * vshCommandOptInt: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Convert option to int. - * On error, a message is displayed. - * - * Return value: - * >0 if option found and valid (@value updated) - * 0 if option not found and not required (@value untouched) - * <0 in all other cases (@value untouched) +/* + * Print usage */ -int -vshCommandOptInt(vshControl *ctl, const vshCmd *cmd, - const char *name, int *value) +static void +virshUsage(void) { - vshCmdOpt *arg; - int ret; - - if ((ret = vshCommandOpt(cmd, name, &arg, true)) <= 0) - return ret; + const vshCmdGrp *grp; + const vshCmdDef *cmd; - if ((ret = virStrToLong_i(arg->data, NULL, 10, value)) < 0) - vshError(ctl, - _("Numeric value '%s' for <%s> option is malformed or out of range"), - arg->data, name); - else - ret = 1; - - return ret; -} - -static int -vshCommandOptUIntInternal(vshControl *ctl, - const vshCmd *cmd, - const char *name, - unsigned int *value, - bool wrap) -{ - vshCmdOpt *arg; - int ret; - - if ((ret = vshCommandOpt(cmd, name, &arg, true)) <= 0) - return ret; - - if (wrap) - ret = virStrToLong_ui(arg->data, NULL, 10, value); - else - ret = virStrToLong_uip(arg->data, NULL, 10, value); - if (ret < 0) - vshError(ctl, - _("Numeric value '%s' for <%s> option is malformed or out of range"), - arg->data, name); - else - ret = 1; - - return ret; -} - -/** - * vshCommandOptUInt: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Convert option to unsigned int, reject negative numbers - * See vshCommandOptInt() - */ -int -vshCommandOptUInt(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned int *value) -{ - return vshCommandOptUIntInternal(ctl, cmd, name, value, false); -} - -/** - * vshCommandOptUIntWrap: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Convert option to unsigned int, wraps negative numbers to positive - * See vshCommandOptInt() - */ -int -vshCommandOptUIntWrap(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned int *value) -{ - return vshCommandOptUIntInternal(ctl, cmd, name, value, true); -} - -static int -vshCommandOptULInternal(vshControl *ctl, - const vshCmd *cmd, - const char *name, - unsigned long *value, - bool wrap) -{ - vshCmdOpt *arg; - int ret; - - if ((ret = vshCommandOpt(cmd, name, &arg, true)) <= 0) - return ret; - - if (wrap) - ret = virStrToLong_ul(arg->data, NULL, 10, value); - else - ret = virStrToLong_ulp(arg->data, NULL, 10, value); - if (ret < 0) - vshError(ctl, - _("Numeric value '%s' for <%s> option is malformed or out of range"), - arg->data, name); - else - ret = 1; - - return ret; -} - -/* - * vshCommandOptUL: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Convert option to unsigned long - * See vshCommandOptInt() - */ -int -vshCommandOptUL(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long *value) -{ - return vshCommandOptULInternal(ctl, cmd, name, value, false); -} - -/** - * vshCommandOptULWrap: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Convert option to unsigned long, wraps negative numbers to positive - * See vshCommandOptInt() - */ -int -vshCommandOptULWrap(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long *value) -{ - return vshCommandOptULInternal(ctl, cmd, name, value, true); -} - -/** - * vshCommandOptString: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Returns option as STRING - * Return value: - * >0 if option found and valid (@value updated) - * 0 if option not found and not required (@value untouched) - * <0 in all other cases (@value untouched) - */ -int -vshCommandOptString(vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd, - const char *name, const char **value) -{ - vshCmdOpt *arg; - int ret; - - if ((ret = vshCommandOpt(cmd, name, &arg, true)) <= 0) - return ret; - - if (!*arg->data && !(arg->def->flags & VSH_OFLAG_EMPTY_OK)) - return -1; - *value = arg->data; - return 1; -} - -/** - * vshCommandOptStringReq: - * @ctl virsh control structure - * @cmd command structure - * @name option name - * @value result (updated to NULL or the option argument) - * - * Gets a option argument as string. - * - * Returns 0 on success or when the option is not present and not - * required, *value is set to the option argument. On error -1 is - * returned and error message printed. - */ -int -vshCommandOptStringReq(vshControl *ctl, - const vshCmd *cmd, - const char *name, - const char **value) -{ - vshCmdOpt *arg; - int ret; - const char *error = NULL; - - /* clear out the value */ - *value = NULL; - - ret = vshCommandOpt(cmd, name, &arg, true); - /* option is not required and not present */ - if (ret == 0) - return 0; - /* this should not be propagated here, just to be sure */ - if (ret == -1) - error = N_("Mandatory option not present"); - else if (!*arg->data && !(arg->def->flags & VSH_OFLAG_EMPTY_OK)) - error = N_("Option argument is empty"); - - if (error) { - vshError(ctl, _("Failed to get option '%s': %s"), name, _(error)); - return -1; - } - - *value = arg->data; - return 0; -} - -/** - * vshCommandOptLongLong: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Returns option as long long - * See vshCommandOptInt() - */ -int -vshCommandOptLongLong(vshControl *ctl, const vshCmd *cmd, - const char *name, long long *value) -{ - vshCmdOpt *arg; - int ret; - - if ((ret = vshCommandOpt(cmd, name, &arg, true)) <= 0) - return ret; - - if ((ret = virStrToLong_ll(arg->data, NULL, 10, value)) < 0) - vshError(ctl, - _("Numeric value '%s' for <%s> option is malformed or out of range"), - arg->data, name); - else - ret = 1; - - return ret; -} - -static int -vshCommandOptULongLongInternal(vshControl *ctl, - const vshCmd *cmd, - const char *name, - unsigned long long *value, - bool wrap) -{ - vshCmdOpt *arg; - int ret; - - if ((ret = vshCommandOpt(cmd, name, &arg, true)) <= 0) - return ret; - - if (wrap) - ret = virStrToLong_ull(arg->data, NULL, 10, value); - else - ret = virStrToLong_ullp(arg->data, NULL, 10, value); - if (ret < 0) - vshError(ctl, - _("Numeric value '%s' for <%s> option is malformed or out of range"), - arg->data, name); - else - ret = 1; - - return ret; -} - -/** - * vshCommandOptULongLong: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Returns option as long long, rejects negative numbers - * See vshCommandOptInt() - */ -int -vshCommandOptULongLong(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long long *value) -{ - return vshCommandOptULongLongInternal(ctl, cmd, name, value, false); -} - -/** - * vshCommandOptULongLongWrap: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * - * Returns option as long long, wraps negative numbers to positive - * See vshCommandOptInt() - */ -int -vshCommandOptULongLongWrap(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long long *value) -{ - return vshCommandOptULongLongInternal(ctl, cmd, name, value, true); -} - -/** - * vshCommandOptScaledInt: - * @ctl virsh control structure - * @cmd command reference - * @name option name - * @value result - * @scale default of 1 or 1024, if no suffix is present - * @max maximum value permitted - * - * Returns option as long long, scaled according to suffix - * See vshCommandOptInt() - */ -int -vshCommandOptScaledInt(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long long *value, - int scale, unsigned long long max) -{ - vshCmdOpt *arg; - char *end; - int ret; - - if ((ret = vshCommandOpt(cmd, name, &arg, true)) <= 0) - return ret; - if (virStrToLong_ullp(arg->data, &end, 10, value) < 0 || - virScaleInteger(value, end, scale, max) < 0) - { - vshError(ctl, - _("Numeric value '%s' for <%s> option is malformed or out of range"), - arg->data, name); - ret = -1; - } else { - ret = 1; - } - - return ret; -} - - -/** - * vshCommandOptBool: - * @cmd command reference - * @name option name - * - * Returns true/false if the option exists. Note that this does NOT - * validate whether the option is actually boolean, or even whether - * name is legal; so that this can be used to probe whether a data - * option is present without actually using that data. - */ -bool -vshCommandOptBool(const vshCmd *cmd, const char *name) -{ - vshCmdOpt *dummy; - - return vshCommandOpt(cmd, name, &dummy, false) == 1; -} - -/** - * vshCommandOptArgv: - * @ctl virsh control structure - * @cmd command reference - * @opt starting point for the search - * - * Returns the next argv argument after OPT (or the first one if OPT - * is NULL), or NULL if no more are present. - * - * Requires that a VSH_OT_ARGV option be last in the - * list of supported options in CMD->def->opts. - */ -const vshCmdOpt * -vshCommandOptArgv(vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd, - const vshCmdOpt *opt) -{ - opt = opt ? opt->next : cmd->opts; - - while (opt) { - if (opt->def->type == VSH_OT_ARGV) - return opt; - opt = opt->next; - } - return NULL; -} - -/* - * vshCommandOptTimeoutToMs: - * @ctl virsh control structure - * @cmd command reference - * @timeout result - * - * Parse an optional --timeout parameter in seconds, but store the - * value of the timeout in milliseconds. - * See vshCommandOptInt() - */ -int -vshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout) -{ - int ret; - unsigned int utimeout; - - if ((ret = vshCommandOptUInt(ctl, cmd, "timeout", &utimeout)) <= 0) - return ret; - - /* Ensure that the timeout is not zero and that we can convert - * it from seconds to milliseconds without overflowing. */ - if (utimeout == 0 || utimeout > INT_MAX / 1000) { - vshError(ctl, - _("Numeric value '%u' for <%s> option is malformed or out of range"), - utimeout, - "timeout"); - ret = -1; - } else { - *timeout = ((int) utimeout) * 1000; - } - - return ret; -} - -static bool -vshConnectionUsability(vshControl *ctl, virConnectPtr conn) -{ - if (!conn || - virConnectIsAlive(conn) == 0) { - vshError(ctl, "%s", _("no valid connection")); - return false; - } - - /* The connection is considered dead only if - * virConnectIsAlive() successfuly says so. - */ - vshResetLibvirtError(); - - return true; -} - -/* - * Executes command(s) and returns return code from last command - */ -static bool -vshCommandRun(vshControl *ctl, const vshCmd *cmd) -{ - bool ret = true; - - while (cmd) { - struct timeval before, after; - bool enable_timing = ctl->timing; - - if ((ctl->conn == NULL || disconnected) && - !(cmd->def->flags & VSH_CMD_FLAG_NOCONNECT)) - vshReconnect(ctl); - - if (enable_timing) - GETTIMEOFDAY(&before); - - if ((cmd->def->flags & VSH_CMD_FLAG_NOCONNECT) || - vshConnectionUsability(ctl, ctl->conn)) { - ret = cmd->def->handler(ctl, cmd); - } else { - /* connection is not usable, return error */ - ret = false; - } - - if (enable_timing) - GETTIMEOFDAY(&after); - - /* try to automatically catch disconnections */ - if (!ret && - ((last_error != NULL) && - (((last_error->code == VIR_ERR_SYSTEM_ERROR) && - (last_error->domain == VIR_FROM_REMOTE)) || - (last_error->code == VIR_ERR_RPC) || - (last_error->code == VIR_ERR_NO_CONNECT) || - (last_error->code == VIR_ERR_INVALID_CONN)))) - disconnected++; - - if (!ret) - vshReportError(ctl); - - if (STREQ(cmd->def->name, "quit") || - STREQ(cmd->def->name, "exit")) /* hack ... */ - return ret; - - if (enable_timing) { - double diff_ms = (((after.tv_sec - before.tv_sec) * 1000.0) + - ((after.tv_usec - before.tv_usec) / 1000.0)); - - vshPrint(ctl, _("\n(Time: %.3f ms)\n\n"), diff_ms); - } else { - vshPrintExtra(ctl, "\n"); - } - cmd = cmd->next; - } - return ret; -} - -/* --------------- - * Command parsing - * --------------- - */ - -typedef enum { - VSH_TK_ERROR, /* Failed to parse a token */ - VSH_TK_ARG, /* Arbitrary argument, might be option or empty */ - VSH_TK_SUBCMD_END, /* Separation between commands */ - VSH_TK_END /* No more commands */ -} vshCommandToken; - -typedef struct _vshCommandParser vshCommandParser; -struct _vshCommandParser { - vshCommandToken(*getNextArg)(vshControl *, vshCommandParser *, - char **); - /* vshCommandStringGetArg() */ - char *pos; - /* vshCommandArgvGetArg() */ - char **arg_pos; - char **arg_end; -}; - -static bool -vshCommandParse(vshControl *ctl, vshCommandParser *parser) -{ - char *tkdata = NULL; - vshCmd *clast = NULL; - vshCmdOpt *first = NULL; - - if (ctl->cmd) { - vshCommandFree(ctl->cmd); - ctl->cmd = NULL; - } - - while (1) { - vshCmdOpt *last = NULL; - const vshCmdDef *cmd = NULL; - vshCommandToken tk; - bool data_only = false; - uint32_t opts_need_arg = 0; - uint32_t opts_required = 0; - uint32_t opts_seen = 0; - - first = NULL; - - while (1) { - const vshCmdOptDef *opt = NULL; - - tkdata = NULL; - tk = parser->getNextArg(ctl, parser, &tkdata); - - if (tk == VSH_TK_ERROR) - goto syntaxError; - if (tk != VSH_TK_ARG) { - VIR_FREE(tkdata); - break; - } - - if (cmd == NULL) { - /* first token must be command name */ - if (!(cmd = vshCmddefSearch(tkdata))) { - vshError(ctl, _("unknown command: '%s'"), tkdata); - goto syntaxError; /* ... or ignore this command only? */ - } - if (vshCmddefOptParse(cmd, &opts_need_arg, - &opts_required) < 0) { - vshError(ctl, - _("internal error: bad options in command: '%s'"), - tkdata); - goto syntaxError; - } - VIR_FREE(tkdata); - } else if (data_only) { - goto get_data; - } else if (tkdata[0] == '-' && tkdata[1] == '-' && - c_isalnum(tkdata[2])) { - char *optstr = strchr(tkdata + 2, '='); - int opt_index = 0; - - if (optstr) { - *optstr = '\0'; /* convert the '=' to '\0' */ - optstr = vshStrdup(ctl, optstr + 1); - } - /* Special case 'help' to ignore all spurious options */ - if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2, - &opts_seen, &opt_index, - &optstr))) { - VIR_FREE(optstr); - if (STREQ(cmd->name, "help")) - continue; - goto syntaxError; - } - VIR_FREE(tkdata); - - if (opt->type != VSH_OT_BOOL) { - /* option data */ - if (optstr) - tkdata = optstr; - else - tk = parser->getNextArg(ctl, parser, &tkdata); - if (tk == VSH_TK_ERROR) - goto syntaxError; - if (tk != VSH_TK_ARG) { - vshError(ctl, - _("expected syntax: --%s <%s>"), - opt->name, - opt->type == - VSH_OT_INT ? _("number") : _("string")); - goto syntaxError; - } - if (opt->type != VSH_OT_ARGV) - opts_need_arg &= ~(1 << opt_index); - } else { - tkdata = NULL; - if (optstr) { - vshError(ctl, _("invalid '=' after option --%s"), - opt->name); - VIR_FREE(optstr); - goto syntaxError; - } - } - } else if (tkdata[0] == '-' && tkdata[1] == '-' && - tkdata[2] == '\0') { - data_only = true; - continue; - } else { - get_data: - /* Special case 'help' to ignore spurious data */ - if (!(opt = vshCmddefGetData(cmd, &opts_need_arg, - &opts_seen)) && - STRNEQ(cmd->name, "help")) { - vshError(ctl, _("unexpected data '%s'"), tkdata); - goto syntaxError; - } - } - if (opt) { - /* save option */ - vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt)); - - arg->def = opt; - arg->data = tkdata; - arg->next = NULL; - tkdata = NULL; - - if (!first) - first = arg; - if (last) - last->next = arg; - last = arg; - - vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n", - cmd->name, - opt->name, - opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"), - opt->type != VSH_OT_BOOL ? arg->data : _("(none)")); - } - } - - /* command parsed -- allocate new struct for the command */ - if (cmd) { - vshCmd *c = vshMalloc(ctl, sizeof(vshCmd)); - vshCmdOpt *tmpopt = first; - - /* if we encountered --help, replace parsed command with - * 'help <cmdname>' */ - for (tmpopt = first; tmpopt; tmpopt = tmpopt->next) { - if (STRNEQ(tmpopt->def->name, "help")) - continue; - - vshCommandOptFree(first); - first = vshMalloc(ctl, sizeof(vshCmdOpt)); - first->def = &(opts_help[0]); - first->data = vshStrdup(ctl, cmd->name); - first->next = NULL; - - cmd = vshCmddefSearch("help"); - opts_required = 0; - opts_seen = 0; - break; - } - - c->opts = first; - c->def = cmd; - c->next = NULL; - - if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) { - VIR_FREE(c); - goto syntaxError; - } - - if (!ctl->cmd) - ctl->cmd = c; - if (clast) - clast->next = c; - clast = c; - } - - if (tk == VSH_TK_END) - break; - } - - return true; - - syntaxError: - if (ctl->cmd) { - vshCommandFree(ctl->cmd); - ctl->cmd = NULL; - } - if (first) - vshCommandOptFree(first); - VIR_FREE(tkdata); - return false; -} - -/* -------------------- - * Command argv parsing - * -------------------- - */ - -static vshCommandToken ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) -vshCommandArgvGetArg(vshControl *ctl, vshCommandParser *parser, char **res) -{ - if (parser->arg_pos == parser->arg_end) { - *res = NULL; - return VSH_TK_END; - } - - *res = vshStrdup(ctl, *parser->arg_pos); - parser->arg_pos++; - return VSH_TK_ARG; -} - -static bool -vshCommandArgvParse(vshControl *ctl, int nargs, char **argv) -{ - vshCommandParser parser; - - if (nargs <= 0) - return false; - - parser.arg_pos = argv; - parser.arg_end = argv + nargs; - parser.getNextArg = vshCommandArgvGetArg; - return vshCommandParse(ctl, &parser); -} - -/* ---------------------- - * Command string parsing - * ---------------------- - */ - -static vshCommandToken ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) -vshCommandStringGetArg(vshControl *ctl, vshCommandParser *parser, char **res) -{ - bool single_quote = false; - bool double_quote = false; - int sz = 0; - char *p = parser->pos; - char *q = vshStrdup(ctl, p); - - *res = q; - - while (*p && (*p == ' ' || *p == '\t')) - p++; - - if (*p == '\0') - return VSH_TK_END; - if (*p == ';') { - parser->pos = ++p; /* = \0 or begin of next command */ - return VSH_TK_SUBCMD_END; - } - - while (*p) { - /* end of token is blank space or ';' */ - if (!double_quote && !single_quote && - (*p == ' ' || *p == '\t' || *p == ';')) - break; - - if (!double_quote && *p == '\'') { /* single quote */ - single_quote = !single_quote; - p++; - continue; - } else if (!single_quote && *p == '\\') { /* escape */ - /* - * The same as the bash, a \ in "" is an escaper, - * but a \ in '' is not an escaper. - */ - p++; - if (*p == '\0') { - vshError(ctl, "%s", _("dangling \\")); - return VSH_TK_ERROR; - } - } else if (!single_quote && *p == '"') { /* double quote */ - double_quote = !double_quote; - p++; - continue; - } - - *q++ = *p++; - sz++; - } - if (double_quote) { - vshError(ctl, "%s", _("missing \"")); - return VSH_TK_ERROR; - } - - *q = '\0'; - parser->pos = p; - return VSH_TK_ARG; -} - -static bool -vshCommandStringParse(vshControl *ctl, char *cmdstr) -{ - vshCommandParser parser; - - if (cmdstr == NULL || *cmdstr == '\0') - return false; - - parser.pos = cmdstr; - parser.getNextArg = vshCommandStringGetArg; - return vshCommandParse(ctl, &parser); -} - -/* --------------- - * Misc utils - * --------------- - */ -int -vshDomainState(vshControl *ctl, virDomainPtr dom, int *reason) -{ - virDomainInfo info; - - if (reason) - *reason = -1; - - if (!ctl->useGetInfo) { - int state; - if (virDomainGetState(dom, &state, reason, 0) < 0) { - virErrorPtr err = virGetLastError(); - if (err && err->code == VIR_ERR_NO_SUPPORT) - ctl->useGetInfo = true; - else - return -1; - } else { - return state; - } - } - - /* fall back to virDomainGetInfo if virDomainGetState is not supported */ - if (virDomainGetInfo(dom, &info) < 0) - return -1; - else - return info.state; -} - -/* Return a non-NULL string representation of a typed parameter; exit - * if we are out of memory. */ -char * -vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item) -{ - int ret = 0; - char *str = NULL; - - switch (item->type) { - case VIR_TYPED_PARAM_INT: - ret = virAsprintf(&str, "%d", item->value.i); - break; - - case VIR_TYPED_PARAM_UINT: - ret = virAsprintf(&str, "%u", item->value.ui); - break; - - case VIR_TYPED_PARAM_LLONG: - ret = virAsprintf(&str, "%lld", item->value.l); - break; - - case VIR_TYPED_PARAM_ULLONG: - ret = virAsprintf(&str, "%llu", item->value.ul); - break; - - case VIR_TYPED_PARAM_DOUBLE: - ret = virAsprintf(&str, "%f", item->value.d); - break; - - case VIR_TYPED_PARAM_BOOLEAN: - str = vshStrdup(ctl, item->value.b ? _("yes") : _("no")); - break; - - case VIR_TYPED_PARAM_STRING: - str = vshStrdup(ctl, item->value.s); - break; - - default: - vshError(ctl, _("unimplemented parameter type %d"), item->type); - } - - if (ret < 0) { - vshError(ctl, "%s", _("Out of memory")); - exit(EXIT_FAILURE); - } - return str; -} - -void -vshDebug(vshControl *ctl, int level, const char *format, ...) -{ - va_list ap; - char *str; - - /* Aligning log levels to that of libvirt. - * Traces with levels >= user-specified-level - * gets logged into file - */ - if (level < ctl->debug) - return; - - va_start(ap, format); - vshOutputLogFile(ctl, level, format, ap); - va_end(ap); - - va_start(ap, format); - if (virVasprintf(&str, format, ap) < 0) { - /* Skip debug messages on low memory */ - va_end(ap); - return; - } - va_end(ap); - fputs(str, stdout); - VIR_FREE(str); -} - -void -vshPrintExtra(vshControl *ctl, const char *format, ...) -{ - va_list ap; - char *str; - - if (ctl && ctl->quiet) - return; - - va_start(ap, format); - if (virVasprintf(&str, format, ap) < 0) { - vshError(ctl, "%s", _("Out of memory")); - va_end(ap); - return; - } - va_end(ap); - fputs(str, stdout); - VIR_FREE(str); -} - - -bool -vshTTYIsInterruptCharacter(vshControl *ctl ATTRIBUTE_UNUSED, - const char chr ATTRIBUTE_UNUSED) -{ -#ifndef WIN32 - if (ctl->istty && - ctl->termattr.c_cc[VINTR] == chr) - return true; -#endif - - return false; -} - - -bool -vshTTYAvailable(vshControl *ctl) -{ - return ctl->istty; -} - - -int -vshTTYDisableInterrupt(vshControl *ctl ATTRIBUTE_UNUSED) -{ -#ifndef WIN32 - struct termios termset = ctl->termattr; - - if (!ctl->istty) - return -1; - - /* check if we need to set the terminal */ - if (termset.c_cc[VINTR] == _POSIX_VDISABLE) - return 0; - - termset.c_cc[VINTR] = _POSIX_VDISABLE; - termset.c_lflag &= ~ICANON; - - if (tcsetattr(STDIN_FILENO, TCSANOW, &termset) < 0) - return -1; -#endif - - return 0; -} - - -int -vshTTYRestore(vshControl *ctl ATTRIBUTE_UNUSED) -{ -#ifndef WIN32 - if (!ctl->istty) - return 0; - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &ctl->termattr) < 0) - return -1; -#endif - - return 0; -} - - -#if !defined(WIN32) && !defined(HAVE_CFMAKERAW) -/* provide fallback in case cfmakeraw isn't available */ -static void -cfmakeraw(struct termios *attr) -{ - attr->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP - | INLCR | IGNCR | ICRNL | IXON); - attr->c_oflag &= ~OPOST; - attr->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - attr->c_cflag &= ~(CSIZE | PARENB); - attr->c_cflag |= CS8; -} -#endif /* !WIN32 && !HAVE_CFMAKERAW */ - - -int -vshTTYMakeRaw(vshControl *ctl ATTRIBUTE_UNUSED, - bool report_errors ATTRIBUTE_UNUSED) -{ -#ifndef WIN32 - struct termios rawattr = ctl->termattr; - char ebuf[1024]; - - if (!ctl->istty) { - if (report_errors) { - vshError(ctl, "%s", - _("unable to make terminal raw: console isn't a tty")); - } - - return -1; - } - - cfmakeraw(&rawattr); - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rawattr) < 0) { - if (report_errors) - vshError(ctl, _("unable to set tty attributes: %s"), - virStrerror(errno, ebuf, sizeof(ebuf))); - return -1; - } -#endif - - return 0; -} - - -void -vshError(vshControl *ctl, const char *format, ...) -{ - va_list ap; - char *str; - - if (ctl != NULL) { - va_start(ap, format); - vshOutputLogFile(ctl, VSH_ERR_ERROR, format, ap); - va_end(ap); - } - - /* Most output is to stdout, but if someone ran virsh 2>&1, then - * printing to stderr will not interleave correctly with stdout - * unless we flush between every transition between streams. */ - fflush(stdout); - fputs(_("error: "), stderr); - - va_start(ap, format); - /* We can't recursively call vshError on an OOM situation, so ignore - failure here. */ - ignore_value(virVasprintf(&str, format, ap)); - va_end(ap); - - fprintf(stderr, "%s\n", NULLSTR(str)); - fflush(stderr); - VIR_FREE(str); -} - - -static void -vshEventLoop(void *opaque) -{ - vshControl *ctl = opaque; - - while (1) { - bool quit; - virMutexLock(&ctl->lock); - quit = ctl->quit; - virMutexUnlock(&ctl->lock); - - if (quit) - break; - - if (virEventRunDefaultImpl() < 0) - vshReportError(ctl); - } -} - - -/* - * Helpers for waiting for a libvirt event. - */ - -/* We want to use SIGINT to cancel a wait; but as signal handlers - * don't have an opaque argument, we have to use static storage. */ -static int vshEventFd = -1; -static struct sigaction vshEventOldAction; - - -/* Signal handler installed in vshEventStart, removed in vshEventCleanup. */ -static void -vshEventInt(int sig ATTRIBUTE_UNUSED, - siginfo_t *siginfo ATTRIBUTE_UNUSED, - void *context ATTRIBUTE_UNUSED) -{ - char reason = VSH_EVENT_INTERRUPT; - if (vshEventFd >= 0) - ignore_value(safewrite(vshEventFd, &reason, 1)); -} - - -/* Event loop handler used to limit length of waiting for any other event. */ -static void -vshEventTimeout(int timer ATTRIBUTE_UNUSED, - void *opaque) -{ - vshControl *ctl = opaque; - char reason = VSH_EVENT_TIMEOUT; - - if (ctl->eventPipe[1] >= 0) - ignore_value(safewrite(ctl->eventPipe[1], &reason, 1)); -} - - -/** - * vshEventStart: - * @ctl virsh command struct - * @timeout_ms max wait time in milliseconds, or 0 for indefinite - * - * Set up a wait for a libvirt event. The wait can be canceled by - * SIGINT or by calling vshEventDone() in your event handler. If - * @timeout_ms is positive, the wait will also end if the timeout - * expires. Call vshEventWait() to block the main thread (the event - * handler runs in the event loop thread). When done (including if - * there was an error registering for an event), use vshEventCleanup() - * to quit waiting. Returns 0 on success, -1 on failure. */ -int -vshEventStart(vshControl *ctl, int timeout_ms) -{ - struct sigaction action; - - assert(ctl->eventPipe[0] == -1 && ctl->eventPipe[1] == -1 && - vshEventFd == -1 && ctl->eventTimerId >= 0); - if (pipe2(ctl->eventPipe, O_CLOEXEC) < 0) { - char ebuf[1024]; - - vshError(ctl, _("failed to create pipe: %s"), - virStrerror(errno, ebuf, sizeof(ebuf))); - return -1; - } - vshEventFd = ctl->eventPipe[1]; - - action.sa_sigaction = vshEventInt; - action.sa_flags = SA_SIGINFO; - sigemptyset(&action.sa_mask); - sigaction(SIGINT, &action, &vshEventOldAction); - - if (timeout_ms) - virEventUpdateTimeout(ctl->eventTimerId, timeout_ms); - - return 0; -} - - -/** - * vshEventDone: - * @ctl virsh command struct - * - * Call this from an event callback to let the main thread quit - * blocking on further events. - */ -void -vshEventDone(vshControl *ctl) -{ - char reason = VSH_EVENT_DONE; - - if (ctl->eventPipe[1] >= 0) - ignore_value(safewrite(ctl->eventPipe[1], &reason, 1)); -} - - -/** - * vshEventWait: - * @ctl virsh command struct - * - * Call this in the main thread after calling vshEventStart() then - * registering for one or more events. This call will block until - * SIGINT, the timeout registered at the start, or until one of your - * event handlers calls vshEventDone(). Returns an enum VSH_EVENT_* - * stating how the wait concluded, or -1 on error. - */ -int -vshEventWait(vshControl *ctl) -{ - char buf; - int rv; - - assert(ctl->eventPipe[0] >= 0); - while ((rv = read(ctl->eventPipe[0], &buf, 1)) < 0 && errno == EINTR); - if (rv != 1) { - char ebuf[1024]; - - if (!rv) - errno = EPIPE; - vshError(ctl, _("failed to determine loop exit status: %s"), - virStrerror(errno, ebuf, sizeof(ebuf))); - return -1; - } - return buf; -} - - -/** - * vshEventCleanup: - * @ctl virsh command struct - * - * Call at the end of any function that has used vshEventStart(), to - * tear down any remaining SIGINT or timeout handlers. - */ -void -vshEventCleanup(vshControl *ctl) -{ - if (vshEventFd >= 0) { - sigaction(SIGINT, &vshEventOldAction, NULL); - vshEventFd = -1; - } - VIR_FORCE_CLOSE(ctl->eventPipe[0]); - VIR_FORCE_CLOSE(ctl->eventPipe[1]); - virEventUpdateTimeout(ctl->eventTimerId, -1); -} - - -/* - * Initialize debug settings. - */ -static void -vshInitDebug(vshControl *ctl) -{ - const char *debugEnv; - - if (ctl->debug == VSH_DEBUG_DEFAULT) { - /* log level not set from commandline, check env variable */ - debugEnv = virGetEnvAllowSUID("VIRSH_DEBUG"); - if (debugEnv) { - int debug; - if (virStrToLong_i(debugEnv, NULL, 10, &debug) < 0 || - debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) { - vshError(ctl, "%s", - _("VIRSH_DEBUG not set with a valid numeric value")); - } else { - ctl->debug = debug; - } - } - } - - if (ctl->logfile == NULL) { - /* log file not set from cmdline */ - debugEnv = virGetEnvBlockSUID("VIRSH_LOG_FILE"); - if (debugEnv && *debugEnv) { - ctl->logfile = vshStrdup(ctl, debugEnv); - vshOpenLogFile(ctl); - } - } -} - -/* - * Initialize connection. - */ -static bool -vshInit(vshControl *ctl) -{ - /* Since we have the commandline arguments parsed, we need to - * re-initialize all the debugging to make it work properly */ - vshInitDebug(ctl); - - if (ctl->conn) - return false; - - /* set up the library error handler */ - virSetErrorFunc(NULL, virshErrorHandler); - - if (virEventRegisterDefaultImpl() < 0) - return false; - - if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0) - return false; - ctl->eventLoopStarted = true; - - if ((ctl->eventTimerId = virEventAddTimeout(-1, vshEventTimeout, ctl, - NULL)) < 0) - return false; - - if (ctl->name) { - vshReconnect(ctl); - /* 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). - */ - if (!ctl->conn) { - vshReportError(ctl); - return false; - } - } - - return true; -} - -#define LOGFILE_FLAGS (O_WRONLY | O_APPEND | O_CREAT | O_SYNC) - -/** - * vshOpenLogFile: - * - * Open log file. - */ -void -vshOpenLogFile(vshControl *ctl) -{ - if (ctl->logfile == NULL) - return; - - if ((ctl->log_fd = open(ctl->logfile, LOGFILE_FLAGS, FILE_MODE)) < 0) { - vshError(ctl, "%s", - _("failed to open the log file. check the log file path")); - exit(EXIT_FAILURE); - } -} - -/** - * vshOutputLogFile: - * - * Outputting an error to log file. - */ -void -vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format, - va_list ap) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *str = NULL; - size_t len; - const char *lvl = ""; - time_t stTime; - struct tm stTm; - - if (ctl->log_fd == -1) - return; - - /** - * create log format - * - * [YYYY.MM.DD HH:MM:SS SIGNATURE PID] LOG_LEVEL message - */ - time(&stTime); - localtime_r(&stTime, &stTm); - virBufferAsprintf(&buf, "[%d.%02d.%02d %02d:%02d:%02d %s %d] ", - (1900 + stTm.tm_year), - (1 + stTm.tm_mon), - stTm.tm_mday, - stTm.tm_hour, - stTm.tm_min, - stTm.tm_sec, - SIGN_NAME, - (int) getpid()); - switch (log_level) { - case VSH_ERR_DEBUG: - lvl = LVL_DEBUG; - break; - case VSH_ERR_INFO: - lvl = LVL_INFO; - break; - case VSH_ERR_NOTICE: - lvl = LVL_INFO; - break; - case VSH_ERR_WARNING: - lvl = LVL_WARNING; - break; - case VSH_ERR_ERROR: - lvl = LVL_ERROR; - break; - default: - lvl = LVL_DEBUG; - break; - } - virBufferAsprintf(&buf, "%s ", lvl); - virBufferVasprintf(&buf, msg_format, ap); - virBufferAddChar(&buf, '\n'); - - if (virBufferError(&buf)) - goto error; - - str = virBufferContentAndReset(&buf); - len = strlen(str); - if (len > 1 && str[len - 2] == '\n') { - str[len - 1] = '\0'; - len--; - } - - /* write log */ - if (safewrite(ctl->log_fd, str, len) < 0) - goto error; - - VIR_FREE(str); - return; - - error: - vshCloseLogFile(ctl); - vshError(ctl, "%s", _("failed to write the log file")); - virBufferFreeAndReset(&buf); - VIR_FREE(str); -} - -/** - * vshCloseLogFile: - * - * Close log file. - */ -void -vshCloseLogFile(vshControl *ctl) -{ - char ebuf[1024]; - - /* log file close */ - if (VIR_CLOSE(ctl->log_fd) < 0) { - vshError(ctl, _("%s: failed to write log file: %s"), - ctl->logfile ? ctl->logfile : "?", - virStrerror(errno, ebuf, sizeof(ebuf))); - } - - if (ctl->logfile) { - VIR_FREE(ctl->logfile); - ctl->logfile = NULL; - } -} - -#if WITH_READLINE - -/* ----------------- - * Readline stuff - * ----------------- - */ - -/* - * Generator function for command completion. STATE lets us - * know whether to start from scratch; without any state - * (i.e. STATE == 0), then we start at the top of the list. - */ -static char * -vshReadlineCommandGenerator(const char *text, int state) -{ - static int grp_list_index, cmd_list_index, len; - const char *name; - const vshCmdGrp *grp; - const vshCmdDef *cmds; - - if (!state) { - grp_list_index = 0; - cmd_list_index = 0; - len = strlen(text); - } - - grp = cmdGroups; - - /* Return the next name which partially matches from the - * command list. - */ - while (grp[grp_list_index].name) { - cmds = grp[grp_list_index].commands; - - if (cmds[cmd_list_index].name) { - while ((name = cmds[cmd_list_index].name)) { - cmd_list_index++; - - if (STREQLEN(name, text, len)) - return vshStrdup(NULL, name); - } - } else { - cmd_list_index = 0; - grp_list_index++; - } - } - - /* If no names matched, then return NULL. */ - return NULL; -} - -static char * -vshReadlineOptionsGenerator(const char *text, int state) -{ - static int list_index, len; - static const vshCmdDef *cmd; - const char *name; - - if (!state) { - /* determine command name */ - char *p; - char *cmdname; - - if (!(p = strchr(rl_line_buffer, ' '))) - return NULL; - - cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1); - memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); - - cmd = vshCmddefSearch(cmdname); - list_index = 0; - len = strlen(text); - VIR_FREE(cmdname); - } - - if (!cmd) - return NULL; - - if (!cmd->opts) - return NULL; - - while ((name = cmd->opts[list_index].name)) { - const vshCmdOptDef *opt = &cmd->opts[list_index]; - char *res; - - list_index++; - - if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV) - /* ignore non --option */ - continue; - - if (len > 2) { - if (STRNEQLEN(name, text + 2, len - 2)) - continue; - } - res = vshMalloc(NULL, strlen(name) + 3); - snprintf(res, strlen(name) + 3, "--%s", name); - return res; - } - - /* If no names matched, then return NULL. */ - return NULL; -} - -static char ** -vshReadlineCompletion(const char *text, int start, - int end ATTRIBUTE_UNUSED) -{ - char **matches = (char **) NULL; - - if (start == 0) - /* command name generator */ - matches = rl_completion_matches(text, vshReadlineCommandGenerator); - else - /* commands options */ - matches = rl_completion_matches(text, vshReadlineOptionsGenerator); - return matches; -} - -# define VIRSH_HISTSIZE_MAX 500000 - -static int -vshReadlineInit(vshControl *ctl) -{ - char *userdir = NULL; - int max_history = 500; - const char *histsize_str; - - /* Allow conditional parsing of the ~/.inputrc file. - * Work around ancient readline 4.1 (hello Mac OS X), - * which declared it as 'char *' instead of 'const char *'. - */ - rl_readline_name = (char *) "virsh"; - - /* Tell the completer that we want a crack first. */ - rl_attempted_completion_function = vshReadlineCompletion; - - /* Limit the total size of the history buffer */ - if ((histsize_str = virGetEnvBlockSUID("VIRSH_HISTSIZE"))) { - if (virStrToLong_i(histsize_str, NULL, 10, &max_history) < 0) { - vshError(ctl, "%s", _("Bad $VIRSH_HISTSIZE value.")); - VIR_FREE(userdir); - return -1; - } else if (max_history > VIRSH_HISTSIZE_MAX || max_history < 0) { - vshError(ctl, _("$VIRSH_HISTSIZE value should be between 0 and %d"), - VIRSH_HISTSIZE_MAX); - VIR_FREE(userdir); - return -1; - } - } - stifle_history(max_history); - - /* Prepare to read/write history from/to the $XDG_CACHE_HOME/virsh/history file */ - userdir = virGetUserCacheDirectory(); - - if (userdir == NULL) { - vshError(ctl, "%s", _("Could not determine home directory")); - return -1; - } - - if (virAsprintf(&ctl->historydir, "%s/virsh", userdir) < 0) { - vshError(ctl, "%s", _("Out of memory")); - VIR_FREE(userdir); - return -1; - } - - if (virAsprintf(&ctl->historyfile, "%s/history", ctl->historydir) < 0) { - vshError(ctl, "%s", _("Out of memory")); - VIR_FREE(userdir); - return -1; - } - - VIR_FREE(userdir); - - read_history(ctl->historyfile); - - return 0; -} - -static void -vshReadlineDeinit(vshControl *ctl) -{ - if (ctl->historyfile != NULL) { - if (virFileMakePathWithMode(ctl->historydir, 0755) < 0 && - errno != EEXIST) { - char ebuf[1024]; - vshError(ctl, _("Failed to create '%s': %s"), - ctl->historydir, virStrerror(errno, ebuf, sizeof(ebuf))); - } else { - write_history(ctl->historyfile); - } - } - - VIR_FREE(ctl->historydir); - VIR_FREE(ctl->historyfile); -} - -static char * -vshReadline(vshControl *ctl ATTRIBUTE_UNUSED, const char *prompt) -{ - return readline(prompt); -} - -#else /* !WITH_READLINE */ - -static int -vshReadlineInit(vshControl *ctl ATTRIBUTE_UNUSED) -{ - /* empty */ - return 0; -} - -static void -vshReadlineDeinit(vshControl *ctl ATTRIBUTE_UNUSED) -{ - /* empty */ -} - -static char * -vshReadline(vshControl *ctl, const char *prompt) -{ - char line[1024]; - char *r; - int len; - - fputs(prompt, stdout); - r = fgets(line, sizeof(line), stdin); - if (r == NULL) return NULL; /* EOF */ - - /* Chomp trailing \n */ - len = strlen(r); - if (len > 0 && r[len-1] == '\n') - r[len-1] = '\0'; - - return vshStrdup(ctl, r); -} - -#endif /* !WITH_READLINE */ - -static void -vshDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) -{ - /* nothing to be done here */ -} - -/* - * Deinitialize virsh - */ -static bool -vshDeinit(vshControl *ctl) -{ - vshReadlineDeinit(ctl); - vshCloseLogFile(ctl); - VIR_FREE(ctl->name); - if (ctl->conn) { - int ret; - virConnectUnregisterCloseCallback(ctl->conn, vshCatchDisconnect); - ret = virConnectClose(ctl->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")); - } - virResetLastError(); - - if (ctl->eventLoopStarted) { - int timer; - - virMutexLock(&ctl->lock); - ctl->quit = true; - /* HACK: Add a dummy timeout to break event loop */ - timer = virEventAddTimeout(0, virshDeinitTimer, NULL, NULL); - virMutexUnlock(&ctl->lock); - - virThreadJoin(&ctl->eventLoop); - - if (timer != -1) - virEventRemoveTimeout(timer); - - if (ctl->eventTimerId != -1) - virEventRemoveTimeout(ctl->eventTimerId); - - ctl->eventLoopStarted = false; - } - - virMutexDestroy(&ctl->lock); - - return true; -} - -/* - * Print usage - */ -static void -virshUsage(void) -{ - const vshCmdGrp *grp; - const vshCmdDef *cmd; - - fprintf(stdout, _("\n%s [options]... [<command_string>]" - "\n%s [options]... <command> [args...]\n\n" - " options:\n" - " -c | --connect=URI hypervisor connection URI\n" - " -d | --debug=NUM debug level [0-4]\n" - " -e | --escape <char> set escape sequence for console\n" - " -h | --help this help\n" - " -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" - " -l | --log=FILE output logging to file\n" - " -q | --quiet quiet mode\n" - " -r | --readonly connect readonly\n" - " -t | --timing print timing information\n" - " -v short version\n" - " -V long version\n" - " --version[=TYPE] version, TYPE is short or long (default short)\n" - " commands (non interactive mode):\n\n"), progname, progname); + fprintf(stdout, _("\n%s [options]... [<command_string>]" + "\n%s [options]... <command> [args...]\n\n" + " options:\n" + " -c | --connect=URI hypervisor connection URI\n" + " -d | --debug=NUM debug level [0-4]\n" + " -e | --escape <char> set escape sequence for console\n" + " -h | --help this help\n" + " -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" + " -l | --log=FILE output logging to file\n" + " -q | --quiet quiet mode\n" + " -r | --readonly connect readonly\n" + " -t | --timing print timing information\n" + " -v short version\n" + " -V long version\n" + " --version[=TYPE] version, TYPE is short or long (default short)\n" + " commands (non interactive mode):\n\n"), progname, + progname); for (grp = cmdGroups; grp->name; grp++) { fprintf(stdout, _(" %s (help keyword '%s')\n"), @@ -3641,15 +1529,11 @@ main(int argc, char **argv) virFileActivateDirOverride(argv[0]); - if (!(progname = strrchr(argv[0], '/'))) - progname = argv[0]; - else - progname++; - if ((defaultConn = virGetEnvBlockSUID("VIRSH_DEFAULT_CONNECT_URI"))) ctl->name = vshStrdup(ctl, defaultConn); - vshInitDebug(ctl); + if (vshInit(ctl, &hooks, cmdGroups) < 0) + exit(EXIT_FAILURE); if (!virshParseArgv(ctl, argc, argv) || !virshInit(ctl)) { @@ -3676,7 +1560,8 @@ main(int argc, char **argv) } do { - const char *prompt = ctl->readonly ? VSH_PROMPT_RO : VSH_PROMPT_RW; + const char *prompt = virshCtl.readonly ? VIRSH_PROMPT_RO + : VIRSH_PROMPT_RW; ctl->cmdstr = vshReadline(ctl, prompt); if (ctl->cmdstr == NULL) diff --git a/tools/virsh.h b/tools/virsh.h index 945eb23..15a938e 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -44,141 +44,9 @@ # define VIR_FROM_THIS VIR_FROM_NONE -# define GETTIMEOFDAY(T) gettimeofday(T, NULL) - -# define VSH_MATCH(FLAG) (flags & (FLAG)) - -/** - * The log configuration - */ -# define MSG_BUFFER 4096 -# define SIGN_NAME "virsh" -# define DIR_MODE (S_IWUSR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) /* 0755 */ -# define FILE_MODE (S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) /* 0644 */ -# define LOCK_MODE (S_IWUSR | S_IRUSR) /* 0600 */ -# define LVL_DEBUG "DEBUG" -# define LVL_INFO "INFO" -# define LVL_NOTICE "NOTICE" -# define LVL_WARNING "WARNING" -# define LVL_ERROR "ERROR" - -/** - * vshErrorLevel: - * - * Indicates the level of a log message - */ -typedef enum { - VSH_ERR_DEBUG = 0, - VSH_ERR_INFO, - VSH_ERR_NOTICE, - VSH_ERR_WARNING, - VSH_ERR_ERROR -} vshErrorLevel; - -# define VSH_DEBUG_DEFAULT VSH_ERR_ERROR - -/* - * virsh command line grammar: - * - * command_line = <command>\n | <command>; <command>; ... - * - * command = <keyword> <option> [--] <data> - * - * option = <bool_option> | <int_option> | <string_option> - * data = <string> - * - * bool_option = --optionname - * int_option = --optionname <number> | --optionname=<number> - * string_option = --optionname <string> | --optionname=<string> - * - * keyword = [a-zA-Z][a-zA-Z-]* - * number = [0-9]+ - * string = ('[^']*'|"([^\\"]|\\.)*"|([^ \t\n\\'"]|\\.))+ - * - */ - -/* - * vshCmdOptType - command option type - */ -typedef enum { - VSH_OT_BOOL, /* optional boolean option */ - VSH_OT_STRING, /* optional string option */ - VSH_OT_INT, /* optional or mandatory int option */ - VSH_OT_DATA, /* string data (as non-option) */ - VSH_OT_ARGV, /* remaining arguments */ - VSH_OT_ALIAS, /* alternate spelling for a later argument */ -} vshCmdOptType; - /* * Command group types */ - -/* - * Command Option Flags - */ -enum { - VSH_OFLAG_NONE = 0, /* without flags */ - VSH_OFLAG_REQ = (1 << 0), /* option required */ - VSH_OFLAG_EMPTY_OK = (1 << 1), /* empty string option allowed */ - VSH_OFLAG_REQ_OPT = (1 << 2), /* --optionname required */ -}; - -/* forward declarations */ -typedef struct _vshCmd vshCmd; -typedef struct _vshCmdDef vshCmdDef; -typedef struct _vshCmdGrp vshCmdGrp; -typedef struct _vshCmdInfo vshCmdInfo; -typedef struct _vshCmdOpt vshCmdOpt; -typedef struct _vshCmdOptDef vshCmdOptDef; -typedef struct _vshControl vshControl; -typedef struct _vshCtrlData vshCtrlData; - -typedef char **(*vshCompleter)(unsigned int flags); - -/* - * vshCmdInfo -- name/value pair for information about command - * - * Commands should have at least the following names: - * "help" - short description of command - * "desc" - description of command, or empty string - */ -struct _vshCmdInfo { - const char *name; /* name of information, or NULL for list end */ - const char *data; /* non-NULL information */ -}; - -/* - * vshCmdOptDef - command option definition - */ -struct _vshCmdOptDef { - const char *name; /* the name of option, or NULL for list end */ - vshCmdOptType type; /* option type */ - unsigned int flags; /* flags */ - const char *help; /* non-NULL help string; or for VSH_OT_ALIAS - * the name of a later public option */ - vshCompleter completer; /* option completer */ - unsigned int completer_flags; /* option completer flags */ -}; - -/* - * vshCmdOpt - command options - * - * After parsing a command, all arguments to the command have been - * collected into a list of these objects. - */ -struct _vshCmdOpt { - const vshCmdOptDef *def; /* non-NULL pointer to option definition */ - char *data; /* allocated data, or NULL for bool option */ - vshCmdOpt *next; -}; - -/* - * Command Usage Flags - */ -enum { - VSH_CMD_FLAG_NOCONNECT = (1 << 0), /* no prior connection needed */ - VSH_CMD_FLAG_ALIAS = (1 << 1), /* command is an alias */ -}; # define VIRSH_CMD_GRP_DOM_MANAGEMENT "Domain Management" # define VIRSH_CMD_GRP_DOM_MONITORING "Domain Monitoring" # define VIRSH_CMD_GRP_STORAGE_POOL "Storage Pool" @@ -192,175 +60,26 @@ enum { # define VIRSH_CMD_GRP_HOST_AND_HV "Host and Hypervisor" # define VIRSH_CMD_GRP_VIRSH "Virsh itself" -/* - * vshCmdDef - command definition - */ -struct _vshCmdDef { - const char *name; /* name of command, or NULL for list end */ - bool (*handler) (vshControl *, const vshCmd *); /* command handler */ - const vshCmdOptDef *opts; /* definition of command options */ - const vshCmdInfo *info; /* details about command */ - unsigned int flags; /* bitwise OR of VSH_CMD_FLAG */ -}; -/* - * vshCmd - parsed command - */ -struct _vshCmd { - const vshCmdDef *def; /* command definition */ - vshCmdOpt *opts; /* list of command arguments */ - vshCmd *next; /* next command */ -}; /* * vshControl */ - char *name; /* connection name */ struct _virshControl { virConnectPtr conn; /* connection to hypervisor (MAY BE NULL) */ - vshCmd *cmd; /* the current command */ - char *cmdstr; /* string with command */ - bool imode; /* interactive mode? */ - bool quiet; /* quiet mode */ - int debug; /* print debug messages? */ - bool timing; /* print timing info? */ bool readonly; /* connect readonly (first time only, not * during explicit connect command) */ - char *logfile; /* log file name */ - int log_fd; /* log file descriptor */ - char *historydir; /* readline history directory name */ - char *historyfile; /* readline history file name */ bool useGetInfo; /* must use virDomainGetInfo, since virDomainGetState is not supported */ bool useSnapshotOld; /* cannot use virDomainSnapshotGetParent or virDomainSnapshotNumChildren */ bool blockJobNoBytes; /* true if _BANDWIDTH_BYTE blockjob flags are missing */ - virThread eventLoop; - virMutex lock; - bool eventLoopStarted; - bool quit; - int eventPipe[2]; /* Write-to-self pipe to end waiting for an - * event to occur */ - int eventTimerId; /* id of event loop timeout registration */ - const char *escapeChar; /* String representation of console escape character */ - - int keepalive_interval; /* Client keepalive interval */ - int keepalive_count; /* Client keepalive count */ - -# ifndef WIN32 - struct termios termattr; /* settings of the tty terminal */ -# endif - bool istty; /* is the terminal a tty */ -}; - -struct _vshCmdGrp { - const char *name; /* name of group, or NULL for list end */ - const char *keyword; /* help keyword */ - const vshCmdDef *commands; }; -void vshError(vshControl *ctl, const char *format, ...) - ATTRIBUTE_FMT_PRINTF(2, 3); -void vshOpenLogFile(vshControl *ctl); -void vshOutputLogFile(vshControl *ctl, int log_level, const char *format, - va_list ap) - ATTRIBUTE_FMT_PRINTF(3, 0); -void vshCloseLogFile(vshControl *ctl); - -virConnectPtr vshConnect(vshControl *ctl, const char *uri, bool readonly); - -const char *vshCmddefGetInfo(const vshCmdDef *cmd, const char *info); -const vshCmdDef *vshCmddefSearch(const char *cmdname); -bool vshCmddefHelp(vshControl *ctl, const char *name); -const vshCmdGrp *vshCmdGrpSearch(const char *grpname); -bool vshCmdGrpHelp(vshControl *ctl, const char *name); - -int vshCommandOptInt(vshControl *ctl, const vshCmd *cmd, - const char *name, int *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptUInt(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned int *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptUIntWrap(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned int *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptUL(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptULWrap(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptString(vshControl *ctl, const vshCmd *cmd, - const char *name, const char **value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptStringReq(vshControl *ctl, const vshCmd *cmd, - const char *name, const char **value) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptLongLong(vshControl *ctl, const vshCmd *cmd, - const char *name, long long *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptULongLong(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long long *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptULongLongWrap(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long long *value) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int vshCommandOptScaledInt(vshControl *ctl, const vshCmd *cmd, - const char *name, unsigned long long *value, - int scale, unsigned long long max) - ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -bool vshCommandOptBool(const vshCmd *cmd, const char *name); -const vshCmdOpt *vshCommandOptArgv(vshControl *ctl, const vshCmd *cmd, - const vshCmdOpt *opt); -int vshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout); - -/* Filter flags for various vshCommandOpt*By() functions */ -typedef enum { - VSH_BYID = (1 << 1), - VSH_BYUUID = (1 << 2), - VSH_BYNAME = (1 << 3), - VSH_BYMAC = (1 << 4), -} vshLookupByFlags; - -/* Given an index, return either the name of that device (non-NULL) or - * of its parent (NULL if a root). */ -typedef const char * (*vshTreeLookup)(int devid, bool parent, void *opaque); -int vshTreePrint(vshControl *ctl, vshTreeLookup lookup, void *opaque, - int num_devices, int devid); - -void vshPrintExtra(vshControl *ctl, const char *format, ...) - ATTRIBUTE_FMT_PRINTF(2, 3); -void vshDebug(vshControl *ctl, int level, const char *format, ...) - ATTRIBUTE_FMT_PRINTF(3, 4); - -/* XXX: add batch support */ -# define vshPrint(_ctl, ...) vshPrintExtra(NULL, __VA_ARGS__) - -/* User visible sort, so we want locale-specific case comparison. */ -# define vshStrcasecmp(S1, S2) strcasecmp(S1, S2) -int vshNameSorter(const void *a, const void *b); - -int vshDomainState(vshControl *ctl, virDomainPtr dom, int *reason); -virTypedParameterPtr vshFindTypedParamByName(const char *name, - virTypedParameterPtr list, - int count); -char *vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - -char *vshEditWriteToTempFile(vshControl *ctl, const char *doc); -int vshEditFile(vshControl *ctl, const char *filename); -char *vshEditReadBackFile(vshControl *ctl, const char *filename); -int vshAskReedit(vshControl *ctl, const char *msg, bool relax_avail); -int vshStreamSink(virStreamPtr st, const char *bytes, size_t nbytes, - void *opaque); -double vshPrettyCapacity(unsigned long long val, const char **unit); -int vshStringToArray(const char *str, char ***array); - /* Typedefs, function prototypes for job progress reporting. * There are used by some long lingering commands like * migrate, dump, save, managedsave. @@ -372,156 +91,9 @@ struct _virshCtrlData { virConnectPtr dconn; }; -/* error handling */ -extern virErrorPtr last_error; -void vshReportError(vshControl *ctl); -void vshResetLibvirtError(void); -void vshSaveLibvirtError(void); - -/* terminal modifications */ -bool vshTTYIsInterruptCharacter(vshControl *ctl, const char chr); -int vshTTYDisableInterrupt(vshControl *ctl); -int vshTTYRestore(vshControl *ctl); -int vshTTYMakeRaw(vshControl *ctl, bool report_errors); -bool vshTTYAvailable(vshControl *ctl); - -/* waiting for events */ -enum { - VSH_EVENT_INTERRUPT, - VSH_EVENT_TIMEOUT, - VSH_EVENT_DONE, -}; -int vshEventStart(vshControl *ctl, int timeout_ms); -void vshEventDone(vshControl *ctl); -int vshEventWait(vshControl *ctl); -void vshEventCleanup(vshControl *ctl); - -/* allocation wrappers */ -void *_vshMalloc(vshControl *ctl, size_t sz, const char *filename, int line); -# define vshMalloc(_ctl, _sz) _vshMalloc(_ctl, _sz, __FILE__, __LINE__) - -void *_vshCalloc(vshControl *ctl, size_t nmemb, size_t sz, - const char *filename, int line); -# define vshCalloc(_ctl, _nmemb, _sz) \ - _vshCalloc(_ctl, _nmemb, _sz, __FILE__, __LINE__) - -char *_vshStrdup(vshControl *ctl, const char *s, const char *filename, - int line); -# define vshStrdup(_ctl, _s) _vshStrdup(_ctl, _s, __FILE__, __LINE__) - -/* Poison the raw allocating identifiers in favor of our vsh variants. */ -# undef malloc -# undef calloc -# undef realloc -# undef strdup -# define malloc use_vshMalloc_instead_of_malloc -# define calloc use_vshCalloc_instead_of_calloc -# define realloc use_vshRealloc_instead_of_realloc -# define strdup use_vshStrdup_instead_of_strdup - -/* Macros to help dealing with mutually exclusive options. */ - -/* VSH_EXCLUSIVE_OPTIONS_EXPR: - * - * @NAME1: String containing the name of the option. - * @EXPR1: Expression to validate the variable (boolean variable) - * @NAME2: String containing the name of the option. - * @EXPR2: Expression to validate the variable (boolean variable) - * - * Reject mutually exclusive command options in virsh. Use the - * provided expression to check the variables. - * - * This helper does an early return and therefore it has to be called - * before anything that would require cleanup. - */ -# define VSH_EXCLUSIVE_OPTIONS_EXPR(NAME1, EXPR1, NAME2, EXPR2) \ - if ((EXPR1) && (EXPR2)) { \ - vshError(ctl, _("Options --%s and --%s are mutually exclusive"), \ - NAME1, NAME2); \ - return false; \ - } - -/* VSH_EXCLUSIVE_OPTIONS: - * - * @NAME1: String containing the name of the option. - * @NAME2: String containing the name of the option. - * - * Reject mutually exclusive command options in virsh. Use the - * vshCommandOptBool call to request them. - * - * This helper does an early return and therefore it has to be called - * before anything that would require cleanup. - */ -# define VSH_EXCLUSIVE_OPTIONS(NAME1, NAME2) \ - VSH_EXCLUSIVE_OPTIONS_EXPR(NAME1, vshCommandOptBool(cmd, NAME1), \ - NAME2, vshCommandOptBool(cmd, NAME2)) -/* VSH_EXCLUSIVE_OPTIONS_VAR: - * - * @VARNAME1: Boolean variable containing the value of the option of same name - * @VARNAME2: Boolean variable containing the value of the option of same name - * - * Reject mutually exclusive command options in virsh. Check in variables that - * contain the value and have same name as the option. - * - * This helper does an early return and therefore it has to be called - * before anything that would require cleanup. - */ -# define VSH_EXCLUSIVE_OPTIONS_VAR(VARNAME1, VARNAME2) \ - VSH_EXCLUSIVE_OPTIONS_EXPR(#VARNAME1, VARNAME1, #VARNAME2, VARNAME2) -/* Macros to help dealing with required options. */ -/* VSH_REQUIRE_OPTION_EXPR: - * - * @NAME1: String containing the name of the option. - * @EXPR1: Expression to validate the variable (boolean variable). - * @NAME2: String containing the name of required option. - * @EXPR2: Expression to validate the variable (boolean variable). - * - * Check if required command options in virsh was set. Use the - * provided expression to check the variables. - * - * This helper does an early return and therefore it has to be called - * before anything that would require cleanup. - */ -# define VSH_REQUIRE_OPTION_EXPR(NAME1, EXPR1, NAME2, EXPR2) \ - do { \ - if ((EXPR1) && !(EXPR2)) { \ - vshError(ctl, _("Option --%s is required by option --%s"), \ - NAME2, NAME1); \ - return false; \ - } \ - } while (0) - -/* VSH_REQUIRE_OPTION: - * - * @NAME1: String containing the name of the option. - * @NAME2: String containing the name of required option. - * - * Check if required command options in virsh was set. Use the - * vshCommandOptBool call to request them. - * - * This helper does an early return and therefore it has to be called - * before anything that would require cleanup. - */ -# define VSH_REQUIRE_OPTION(NAME1, NAME2) \ - VSH_REQUIRE_OPTION_EXPR(NAME1, vshCommandOptBool(cmd, NAME1), \ - NAME2, vshCommandOptBool(cmd, NAME2)) -/* VSH_REQUIRE_OPTION_VAR: - * - * @VARNAME1: Boolean variable containing the value of the option of same name. - * @VARNAME2: Boolean variable containing the value of required option of - * same name. - * - * Check if required command options in virsh was set. Check in variables - * that contain the value and have same name as the option. - * - * This helper does an early return and therefore it has to be called - * before anything that would require cleanup. - */ -# define VSH_REQUIRE_OPTION_VAR(VARNAME1, VARNAME2) \ - VSH_REQUIRE_OPTION_EXPR(#VARNAME1, VARNAME1, #VARNAME2, VARNAME2) #endif /* VIRSH_H */ diff --git a/tools/vsh.c b/tools/vsh.c index b1d8dbc..71c8344 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -1998,7 +1998,7 @@ vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format, stTm.tm_hour, stTm.tm_min, stTm.tm_sec, - SIGN_NAME, + ctl->progname, (int) getpid()); switch (log_level) { case VSH_ERR_DEBUG: -- 1.9.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list