qemu-patch-raspberry4/cmd.c
Kevin Wolf 734c3b85cb qemu-io: Don't use global bs in command implementations
Pass in the BlockDriverState to the command handlers instead of using
the global variable. This is an important step to make the commands
usable outside of qemu-io.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2013-06-06 11:27:04 +02:00

554 lines
11 KiB
C

/*
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/time.h>
#include <getopt.h>
#include "cmd.h"
#include "block/aio.h"
#include "qemu/main-loop.h"
#define _(x) x /* not gettext support yet */
/* from libxcmd/command.c */
cmdinfo_t *cmdtab;
int ncmds;
static checkfunc_t check_func;
static int ncmdline;
static char **cmdline;
static int
compare(const void *a, const void *b)
{
return strcmp(((const cmdinfo_t *)a)->name,
((const cmdinfo_t *)b)->name);
}
void add_command(const cmdinfo_t *ci)
{
cmdtab = g_realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab));
cmdtab[ncmds - 1] = *ci;
qsort(cmdtab, ncmds, sizeof(*cmdtab), compare);
}
static int
check_command(
const cmdinfo_t *ci)
{
if (check_func)
return check_func(qemuio_bs, ci);
return 1;
}
void
add_check_command(
checkfunc_t cf)
{
check_func = cf;
}
int
command_usage(
const cmdinfo_t *ci)
{
printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
return 0;
}
int
command(
const cmdinfo_t *ct,
int argc,
char **argv)
{
char *cmd = argv[0];
if (!check_command(ct))
return 0;
if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) {
if (ct->argmax == -1)
fprintf(stderr,
_("bad argument count %d to %s, expected at least %d arguments\n"),
argc-1, cmd, ct->argmin);
else if (ct->argmin == ct->argmax)
fprintf(stderr,
_("bad argument count %d to %s, expected %d arguments\n"),
argc-1, cmd, ct->argmin);
else
fprintf(stderr,
_("bad argument count %d to %s, expected between %d and %d arguments\n"),
argc-1, cmd, ct->argmin, ct->argmax);
return 0;
}
optind = 0;
return ct->cfunc(qemuio_bs, argc, argv);
}
const cmdinfo_t *
find_command(
const char *cmd)
{
cmdinfo_t *ct;
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
if (strcmp(ct->name, cmd) == 0 ||
(ct->altname && strcmp(ct->altname, cmd) == 0))
return (const cmdinfo_t *)ct;
}
return NULL;
}
void add_user_command(char *optarg)
{
cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *));
cmdline[ncmdline-1] = optarg;
}
static void prep_fetchline(void *opaque)
{
int *fetchable = opaque;
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
*fetchable= 1;
}
static char *get_prompt(void);
void command_loop(void)
{
int c, i, done = 0, fetchable = 0, prompted = 0;
char *input;
char **v;
const cmdinfo_t *ct;
for (i = 0; !done && i < ncmdline; i++) {
input = strdup(cmdline[i]);
if (!input) {
fprintf(stderr, _("cannot strdup command '%s': %s\n"),
cmdline[i], strerror(errno));
exit(1);
}
v = breakline(input, &c);
if (c) {
ct = find_command(v[0]);
if (ct) {
done = command(ct, c, v);
} else {
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
}
}
doneline(input, v);
}
if (cmdline) {
g_free(cmdline);
return;
}
while (!done) {
if (!prompted) {
printf("%s", get_prompt());
fflush(stdout);
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
prompted = 1;
}
main_loop_wait(false);
if (!fetchable) {
continue;
}
input = fetchline();
if (input == NULL) {
break;
}
v = breakline(input, &c);
if (c) {
ct = find_command(v[0]);
if (ct) {
done = command(ct, c, v);
} else {
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
}
}
doneline(input, v);
prompted = 0;
fetchable = 0;
}
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
}
/* from libxcmd/input.c */
#if defined(ENABLE_READLINE)
# include <readline/history.h>
# include <readline/readline.h>
#elif defined(ENABLE_EDITLINE)
# include <histedit.h>
#endif
static char *
get_prompt(void)
{
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
if (!prompt[0])
snprintf(prompt, sizeof(prompt), "%s> ", progname);
return prompt;
}
#if defined(ENABLE_READLINE)
char *
fetchline(void)
{
char *line;
line = readline(get_prompt());
if (line && *line)
add_history(line);
return line;
}
#elif defined(ENABLE_EDITLINE)
static char *el_get_prompt(EditLine *e) { return get_prompt(); }
char *
fetchline(void)
{
static EditLine *el;
static History *hist;
HistEvent hevent;
char *line;
int count;
if (!el) {
hist = history_init();
history(hist, &hevent, H_SETSIZE, 100);
el = el_init(progname, stdin, stdout, stderr);
el_source(el, NULL);
el_set(el, EL_SIGNAL, 1);
el_set(el, EL_PROMPT, el_get_prompt);
el_set(el, EL_HIST, history, (const char *)hist);
}
line = strdup(el_gets(el, &count));
if (line) {
if (count > 0)
line[count-1] = '\0';
if (*line)
history(hist, &hevent, H_ENTER, line);
}
return line;
}
#else
# define MAXREADLINESZ 1024
char *
fetchline(void)
{
char *p, *line = malloc(MAXREADLINESZ);
if (!line)
return NULL;
if (!fgets(line, MAXREADLINESZ, stdin)) {
free(line);
return NULL;
}
p = line + strlen(line);
if (p != line && p[-1] == '\n')
p[-1] = '\0';
return line;
}
#endif
static char *qemu_strsep(char **input, const char *delim)
{
char *result = *input;
if (result != NULL) {
char *p;
for (p = result; *p != '\0'; p++) {
if (strchr(delim, *p)) {
break;
}
}
if (*p == '\0') {
*input = NULL;
} else {
*p = '\0';
*input = p + 1;
}
}
return result;
}
char **breakline(char *input, int *count)
{
int c = 0;
char *p;
char **rval = calloc(sizeof(char *), 1);
char **tmp;
while (rval && (p = qemu_strsep(&input, " ")) != NULL) {
if (!*p) {
continue;
}
c++;
tmp = realloc(rval, sizeof(*rval) * (c + 1));
if (!tmp) {
free(rval);
rval = NULL;
c = 0;
break;
} else {
rval = tmp;
}
rval[c - 1] = p;
rval[c] = NULL;
}
*count = c;
return rval;
}
void
doneline(
char *input,
char **vec)
{
free(input);
free(vec);
}
#define EXABYTES(x) ((long long)(x) << 60)
#define PETABYTES(x) ((long long)(x) << 50)
#define TERABYTES(x) ((long long)(x) << 40)
#define GIGABYTES(x) ((long long)(x) << 30)
#define MEGABYTES(x) ((long long)(x) << 20)
#define KILOBYTES(x) ((long long)(x) << 10)
#define TO_EXABYTES(x) ((x) / EXABYTES(1))
#define TO_PETABYTES(x) ((x) / PETABYTES(1))
#define TO_TERABYTES(x) ((x) / TERABYTES(1))
#define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
#define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
#define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
void
cvtstr(
double value,
char *str,
size_t size)
{
char *trim;
const char *suffix;
if (value >= EXABYTES(1)) {
suffix = " EiB";
snprintf(str, size - 4, "%.3f", TO_EXABYTES(value));
} else if (value >= PETABYTES(1)) {
suffix = " PiB";
snprintf(str, size - 4, "%.3f", TO_PETABYTES(value));
} else if (value >= TERABYTES(1)) {
suffix = " TiB";
snprintf(str, size - 4, "%.3f", TO_TERABYTES(value));
} else if (value >= GIGABYTES(1)) {
suffix = " GiB";
snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value));
} else if (value >= MEGABYTES(1)) {
suffix = " MiB";
snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value));
} else if (value >= KILOBYTES(1)) {
suffix = " KiB";
snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value));
} else {
suffix = " bytes";
snprintf(str, size - 6, "%f", value);
}
trim = strstr(str, ".000");
if (trim) {
strcpy(trim, suffix);
} else {
strcat(str, suffix);
}
}
struct timeval
tsub(struct timeval t1, struct timeval t2)
{
t1.tv_usec -= t2.tv_usec;
if (t1.tv_usec < 0) {
t1.tv_usec += 1000000;
t1.tv_sec--;
}
t1.tv_sec -= t2.tv_sec;
return t1;
}
double
tdiv(double value, struct timeval tv)
{
return value / ((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
}
#define HOURS(sec) ((sec) / (60 * 60))
#define MINUTES(sec) (((sec) % (60 * 60)) / 60)
#define SECONDS(sec) ((sec) % 60)
void
timestr(
struct timeval *tv,
char *ts,
size_t size,
int format)
{
double usec = (double)tv->tv_usec / 1000000.0;
if (format & TERSE_FIXED_TIME) {
if (!HOURS(tv->tv_sec)) {
snprintf(ts, size, "%u:%02u.%02u",
(unsigned int) MINUTES(tv->tv_sec),
(unsigned int) SECONDS(tv->tv_sec),
(unsigned int) (usec * 100));
return;
}
format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */
}
if ((format & VERBOSE_FIXED_TIME) || tv->tv_sec) {
snprintf(ts, size, "%u:%02u:%02u.%02u",
(unsigned int) HOURS(tv->tv_sec),
(unsigned int) MINUTES(tv->tv_sec),
(unsigned int) SECONDS(tv->tv_sec),
(unsigned int) (usec * 100));
} else {
snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000));
}
}
/* from libxcmd/quit.c */
static cmdinfo_t quit_cmd;
/* ARGSUSED */
static int
quit_f(
BlockDriverState *bs,
int argc,
char **argv)
{
return 1;
}
void
quit_init(void)
{
quit_cmd.name = _("quit");
quit_cmd.altname = _("q");
quit_cmd.cfunc = quit_f;
quit_cmd.argmin = -1;
quit_cmd.argmax = -1;
quit_cmd.flags = CMD_FLAG_GLOBAL;
quit_cmd.oneline = _("exit the program");
add_command(&quit_cmd);
}
/* from libxcmd/help.c */
static cmdinfo_t help_cmd;
static void help_onecmd(const char *cmd, const cmdinfo_t *ct);
static void help_oneline(const char *cmd, const cmdinfo_t *ct);
static void
help_all(void)
{
const cmdinfo_t *ct;
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++)
help_oneline(ct->name, ct);
printf(_("\nUse 'help commandname' for extended help.\n"));
}
static int
help_f(
BlockDriverState *bs,
int argc,
char **argv)
{
const cmdinfo_t *ct;
if (argc == 1) {
help_all();
return 0;
}
ct = find_command(argv[1]);
if (ct == NULL) {
printf(_("command %s not found\n"), argv[1]);
return 0;
}
help_onecmd(argv[1], ct);
return 0;
}
static void
help_onecmd(
const char *cmd,
const cmdinfo_t *ct)
{
help_oneline(cmd, ct);
if (ct->help)
ct->help();
}
static void
help_oneline(
const char *cmd,
const cmdinfo_t *ct)
{
if (cmd)
printf("%s ", cmd);
else {
printf("%s ", ct->name);
if (ct->altname)
printf("(or %s) ", ct->altname);
}
if (ct->args)
printf("%s ", ct->args);
printf("-- %s\n", ct->oneline);
}
void
help_init(void)
{
help_cmd.name = _("help");
help_cmd.altname = _("?");
help_cmd.cfunc = help_f;
help_cmd.argmin = 0;
help_cmd.argmax = 1;
help_cmd.flags = CMD_FLAG_GLOBAL;
help_cmd.args = _("[command]");
help_cmd.oneline = _("help for one or all commands");
add_command(&help_cmd);
}