2005-11-10 10:12:31 -06:00
|
|
|
/*
|
2005-12-05 05:16:07 -06:00
|
|
|
* virsh.c: a Xen shell used to exercise the libvir API
|
2005-11-10 10:12:31 -06:00
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* See COPYING.LIB for the License of this software
|
|
|
|
*
|
|
|
|
* Daniel Veillard <veillard@redhat.com>
|
|
|
|
*/
|
|
|
|
|
2005-12-08 04:23:34 -06:00
|
|
|
#define _GNU_SOURCE /* isblank() */
|
|
|
|
|
2005-12-05 05:16:07 -06:00
|
|
|
#include "libvir.h"
|
2005-11-10 10:12:31 -06:00
|
|
|
#include <stdio.h>
|
2005-12-08 04:23:34 -06:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
2005-12-01 10:35:42 -06:00
|
|
|
#include <unistd.h>
|
2005-12-08 04:23:34 -06:00
|
|
|
#include <getopt.h>
|
2005-12-01 10:35:42 -06:00
|
|
|
#include <sys/types.h>
|
2005-12-08 04:23:34 -06:00
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include <readline/readline.h>
|
|
|
|
#include <readline/history.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
static char *progname;
|
|
|
|
|
|
|
|
#ifndef TRUE
|
|
|
|
#define TRUE 1
|
|
|
|
#define FALSE 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define VSH_PROMPT_RW "virsh # "
|
|
|
|
#define VSH_PROMPT_RO "virsh > "
|
|
|
|
|
|
|
|
#define GETTIMEOFDAY(T) gettimeofday(T, NULL)
|
|
|
|
#define DIFF_MSEC(T, U) \
|
|
|
|
((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 + \
|
|
|
|
((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0)
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
VSH_MESG, /* standard output */
|
|
|
|
VSH_HEADER, /* header for standard output */
|
|
|
|
VSH_FOOTER, /* timing, last command state, or whatever */
|
|
|
|
VSH_DEBUG1, /* debugN where 'N' = level */
|
|
|
|
VSH_DEBUG2,
|
|
|
|
VSH_DEBUG3,
|
|
|
|
VSH_DEBUG4,
|
|
|
|
VSH_DEBUG5
|
|
|
|
} vshOutType;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
* string_option = --optionname <string>
|
|
|
|
*
|
|
|
|
* keyword = [a-zA-Z]
|
|
|
|
* number = [0-9]+
|
|
|
|
* string = [^[:blank:]] | "[[:alnum:]]"$
|
|
|
|
*
|
|
|
|
* Note: only one <data> token per command is supported. It means:
|
|
|
|
* "command aaa bbb" is unsupported and you have to use any option, like:
|
|
|
|
* "command --aaa <data> bbb" or whatever.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vshCmdOptType - command option type
|
|
|
|
*/
|
|
|
|
typedef enum {
|
|
|
|
VSH_OT_NONE = 0, /* none */
|
|
|
|
VSH_OT_BOOL, /* boolean option */
|
|
|
|
VSH_OT_STRING, /* string option */
|
|
|
|
VSH_OT_INT, /* int option */
|
|
|
|
VSH_OT_DATA /* string data (as non-option) */
|
|
|
|
} vshCmdOptType;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Command Option Flags
|
|
|
|
*/
|
|
|
|
#define VSH_OFLAG_NONE 0 /* without flags */
|
|
|
|
#define VSH_OFLAG_REQ (1 << 1) /* option required */
|
|
|
|
|
|
|
|
/* dummy */
|
|
|
|
typedef struct __vshControl vshControl;
|
|
|
|
typedef struct __vshCmd vshCmd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vshCmdInfo -- information about command
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
char *name; /* name of information */
|
|
|
|
char *data; /* information */
|
|
|
|
} vshCmdInfo;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vshCmdOptDef - command option definition
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
char *name; /* the name of option */
|
|
|
|
vshCmdOptType type; /* option type */
|
|
|
|
int flag; /* flags */
|
|
|
|
char *help; /* help string */
|
|
|
|
} vshCmdOptDef;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vshCmdOpt - command options
|
|
|
|
*/
|
|
|
|
typedef struct vshCmdOpt {
|
|
|
|
vshCmdOptDef *def; /* pointer to relevant option */
|
|
|
|
char *data; /* allocated data */
|
|
|
|
struct vshCmdOpt *next;
|
|
|
|
} vshCmdOpt;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vshCmdDef - command definition
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
char *name;
|
|
|
|
int (*handler)(vshControl *, vshCmd *); /* command handler */
|
|
|
|
vshCmdOptDef *opts; /* definition of command options */
|
|
|
|
vshCmdInfo *info; /* details about command */
|
|
|
|
} vshCmdDef;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vshCmd - parsed command
|
|
|
|
*/
|
|
|
|
typedef struct __vshCmd {
|
|
|
|
vshCmdDef *def; /* command definition */
|
|
|
|
vshCmdOpt *opts; /* list of command arguments */
|
|
|
|
struct __vshCmd *next; /* next command */
|
|
|
|
} __vshCmd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vshControl
|
|
|
|
*/
|
|
|
|
typedef struct __vshControl {
|
|
|
|
virConnectPtr conn; /* connection to hypervisor */
|
|
|
|
vshCmd *cmd; /* the current command */
|
|
|
|
char *cmdstr; /* string with command */
|
|
|
|
uid_t uid; /* process owner */
|
|
|
|
int imode; /* interactive mode? */
|
|
|
|
int quiet; /* quiet mode */
|
|
|
|
int debug; /* print debug messages? */
|
|
|
|
int timing; /* print timing info? */
|
|
|
|
} __vshControl;
|
2005-11-10 10:12:31 -06:00
|
|
|
|
2005-12-02 08:16:21 -06:00
|
|
|
|
2005-12-08 04:23:34 -06:00
|
|
|
static vshCmdDef commands[];
|
|
|
|
|
|
|
|
static void vshError(vshControl *ctl, int doexit, char *format, ...);
|
|
|
|
static int vshInit(vshControl *ctl);
|
|
|
|
static int vshDeinit(vshControl *ctl);
|
|
|
|
static void vshUsage(vshControl *ctl, char *cmdname);
|
|
|
|
|
|
|
|
static int vshParseArgv(vshControl *ctl, int argc, char **argv);
|
|
|
|
|
|
|
|
static char *vshCmddefGetInfo(vshCmdDef *cmd, char *info);
|
|
|
|
static vshCmdDef *vshCmddefSearch(char *cmdname);
|
|
|
|
static int vshCmddefHelp(vshControl *ctl, char *name, int withprog);
|
|
|
|
|
|
|
|
static vshCmdOpt *vshCommandOpt(vshCmd *cmd, char *name);
|
|
|
|
static int vshCommandOptInt(vshCmd *cmd, char *name, int *found);
|
|
|
|
static char *vshCommandOptString(vshCmd *cmd, char *name, int *found);
|
|
|
|
static int vshCommandOptBool(vshCmd *cmd, char *name);
|
|
|
|
|
|
|
|
static void vshPrint(vshControl *ctl, vshOutType out, char *format, ...);
|
|
|
|
|
|
|
|
static char *vshDomainStateToString(int state);
|
|
|
|
static int vshConnectionUsability(vshControl *ctl, virConnectPtr conn, int showerror);
|
|
|
|
|
|
|
|
/* ---------------
|
|
|
|
* Commands
|
|
|
|
* ---------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "help" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_help[] = {
|
|
|
|
{ "syntax", "help [<command>]" },
|
|
|
|
{ "help", "print help" },
|
|
|
|
{ "desc", "Prints global help or command specific help." },
|
2005-12-08 07:26:52 -06:00
|
|
|
{ "version", "Prints versionning informations." },
|
2005-12-08 04:23:34 -06:00
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static vshCmdOptDef opts_help[] = {
|
|
|
|
{ "command", VSH_OT_DATA, 0, "name of command" },
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdHelp(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
char *cmdname = vshCommandOptString(cmd, "command", NULL);
|
|
|
|
|
|
|
|
if (!cmdname) {
|
|
|
|
vshCmdDef *def;
|
|
|
|
|
|
|
|
vshPrint(ctl, VSH_HEADER, "Commands:\n\n");
|
|
|
|
for(def = commands; def->name; def++)
|
|
|
|
vshPrint(ctl, VSH_MESG, " %-15s %s\n", def->name,
|
|
|
|
vshCmddefGetInfo(def, "help"));
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return vshCmddefHelp(ctl, cmdname, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "connect" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_connect[] = {
|
|
|
|
{ "syntax", "connect [--readonly]" },
|
|
|
|
{ "help", "(re)connect to hypervisor" },
|
|
|
|
{ "desc", "Connect to local hypervisor. This is build-in command after shell start up." },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static vshCmdOptDef opts_connect[] = {
|
|
|
|
{ "readonly", VSH_OT_BOOL, 0, "read-only connection" },
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdConnect(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
int ro = vshCommandOptBool(cmd, "readonly");
|
|
|
|
|
|
|
|
if (ctl->conn) {
|
|
|
|
if (virConnectClose(ctl->conn)!=0) {
|
|
|
|
vshError(ctl, FALSE, "failed to disconnect from the hypervisor");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
ctl->conn = NULL;
|
|
|
|
}
|
|
|
|
if (!ro)
|
|
|
|
ctl->conn = virConnectOpen(NULL);
|
|
|
|
else
|
|
|
|
ctl->conn = virConnectOpenReadOnly(NULL);
|
|
|
|
|
|
|
|
if (!ctl->conn)
|
|
|
|
vshError(ctl, FALSE, "failed to connect to the hypervisor");
|
|
|
|
|
|
|
|
return ctl->conn ? TRUE : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "list" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_list[] = {
|
|
|
|
{ "syntax", "list" },
|
|
|
|
{ "help", "list domains" },
|
|
|
|
{ "desc", "Returns list of domains." },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdList(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
int *ids, maxid, i;
|
|
|
|
|
|
|
|
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
maxid = virConnectNumOfDomains(ctl->conn);
|
|
|
|
if (maxid <= 0) {
|
|
|
|
/* strange, there should be at least dom0... */
|
|
|
|
vshError(ctl, FALSE, "failed to list active domains.");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
ids = malloc(sizeof(int) * maxid);
|
|
|
|
virConnectListDomains(ctl->conn, &ids[0], maxid);
|
|
|
|
|
|
|
|
vshPrint(ctl, VSH_HEADER, "%3s %-20s %s\n", "Id", "Name", "State");
|
|
|
|
vshPrint(ctl, VSH_HEADER, "----------------------------------\n");
|
|
|
|
|
|
|
|
for(i=0; i < maxid; i++) {
|
|
|
|
int ret;
|
|
|
|
virDomainInfo info;
|
|
|
|
virDomainPtr dom = virDomainLookupByID(ctl->conn, ids[i]);
|
|
|
|
|
|
|
|
/* this kind of work with domains is not atomic operation */
|
|
|
|
if (!dom)
|
|
|
|
continue;
|
|
|
|
ret = virDomainGetInfo(dom, &info);
|
|
|
|
|
|
|
|
vshPrint(ctl, VSH_MESG, "%3d %-20s %s\n",
|
|
|
|
virDomainGetID(dom),
|
|
|
|
virDomainGetName(dom),
|
|
|
|
ret < 0 ? "no state" : vshDomainStateToString(info.state));
|
|
|
|
/*TODO: virDomainFree(dom); */
|
|
|
|
}
|
|
|
|
free(ids);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "dstate" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_dstate[] = {
|
|
|
|
{ "syntax", "dstate [--id <number> | --name <string> ]" },
|
|
|
|
{ "help", "domain state" },
|
|
|
|
{ "desc", "Returns state about the domain." },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static vshCmdOptDef opts_dstate[] = {
|
|
|
|
{ "name", VSH_OT_STRING, 0, "domain name" },
|
|
|
|
{ "id", VSH_OT_INT, 0, "domain id" },
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdDstate(vshControl *ctl, vshCmd *cmd) {
|
2005-12-05 12:14:37 -06:00
|
|
|
virDomainInfo info;
|
2005-12-08 04:23:34 -06:00
|
|
|
virDomainPtr dom;
|
|
|
|
int found, ret = TRUE;
|
|
|
|
char *name = vshCommandOptString(cmd, "name", NULL);
|
|
|
|
int id = vshCommandOptInt(cmd, "id", &found);
|
|
|
|
|
|
|
|
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
|
|
|
|
return FALSE;
|
2005-12-05 12:14:37 -06:00
|
|
|
|
2005-12-08 04:23:34 -06:00
|
|
|
if (found) {
|
|
|
|
if (!(dom = virDomainLookupByID(ctl->conn, id)))
|
|
|
|
vshError(ctl, FALSE, "failed to get domain '%d'", id);
|
2005-12-05 12:14:37 -06:00
|
|
|
} else {
|
2005-12-08 04:23:34 -06:00
|
|
|
if (!(dom = virDomainLookupByName(ctl->conn, name)))
|
|
|
|
vshError(ctl, FALSE, "failed to get domain '%s'", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dom)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (virDomainGetInfo(dom, &info)==0)
|
|
|
|
vshPrint(ctl, VSH_MESG, "%s\n", vshDomainStateToString(info.state));
|
|
|
|
else
|
|
|
|
ret = FALSE;
|
|
|
|
|
|
|
|
/*TODO: virDomainFree(dom); */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "dinfo" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_dinfo[] = {
|
|
|
|
{ "syntax", "dinfo [--id <number> | --name <string> ]" },
|
|
|
|
{ "help", "domain information" },
|
|
|
|
{ "desc", "Returns basic information about the domain." },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static vshCmdOptDef opts_dinfo[] = {
|
|
|
|
{ "name", VSH_OT_STRING, 0, "domain name" },
|
|
|
|
{ "id", VSH_OT_INT, 0, "domain id" },
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdDinfo(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
virDomainInfo info;
|
|
|
|
virDomainPtr dom;
|
|
|
|
int found, ret = TRUE;
|
|
|
|
char *name = vshCommandOptString(cmd, "name", NULL);
|
|
|
|
int id = vshCommandOptInt(cmd, "id", &found);
|
|
|
|
|
|
|
|
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
if (!(dom = virDomainLookupByID(ctl->conn, id)))
|
|
|
|
vshError(ctl, FALSE, "failed to get domain '%d'", id);
|
|
|
|
} else {
|
|
|
|
if (!(dom = virDomainLookupByName(ctl->conn, name)))
|
|
|
|
vshError(ctl, FALSE, "failed to get domain '%s'", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dom)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (virDomainGetInfo(dom, &info)==0) {
|
|
|
|
vshPrint(ctl, VSH_MESG, "%-15s %d\n", "Id:",
|
|
|
|
virDomainGetID(dom));
|
|
|
|
vshPrint(ctl, VSH_MESG, "%-15s %s\n", "Name:",
|
|
|
|
virDomainGetName(dom));
|
|
|
|
vshPrint(ctl, VSH_MESG, "%-15s %s\n", "State:",
|
|
|
|
vshDomainStateToString(info.state));
|
|
|
|
vshPrint(ctl, VSH_MESG, "%-15s %d\n", "CPU(s):",
|
|
|
|
info.nrVirtCpu);
|
|
|
|
|
|
|
|
if (info.cpuTime != 0)
|
|
|
|
{
|
|
|
|
float cpuUsed = info.cpuTime;
|
|
|
|
cpuUsed /= 1000000000;
|
|
|
|
|
|
|
|
vshPrint(ctl, VSH_MESG, "%-15s %.1fs\n", "CPU time:", cpuUsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
vshPrint(ctl, VSH_MESG, "%-15s %lu kB\n", "Max memory:",
|
|
|
|
info.maxMem);
|
|
|
|
vshPrint(ctl, VSH_MESG, "%-15s %lu kB\n", "Used memory:",
|
|
|
|
info.memory);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
ret = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*TODO: virDomainFree(dom); */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "nameof" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_nameof[] = {
|
|
|
|
{ "syntax", "nameof <id>" },
|
|
|
|
{ "help", "convert domain Id to domain name" },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static vshCmdOptDef opts_nameof[] = {
|
|
|
|
{ "id", VSH_OT_DATA, 0, "domain Id" },
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdNameof(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
int found;
|
|
|
|
int id = vshCommandOptInt(cmd, "id", &found);
|
|
|
|
virDomainPtr dom;
|
|
|
|
|
|
|
|
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
if (!found)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
dom = virDomainLookupByID(ctl->conn, id);
|
|
|
|
if (dom) {
|
|
|
|
vshPrint(ctl, VSH_MESG, "%s\n", virDomainGetName(dom));
|
|
|
|
/*TODO: virDomainFree(dom); */
|
|
|
|
} else {
|
|
|
|
vshError(ctl, FALSE, "failed to get domain '%d'", id);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "idof" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_idof[] = {
|
|
|
|
{ "syntax", "idof <name>" },
|
|
|
|
{ "help", "convert domain name to domain Id" },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static vshCmdOptDef opts_idof[] = {
|
|
|
|
{ "name", VSH_OT_DATA, 0, "domain name" },
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdIdof(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
char *name = vshCommandOptString(cmd, "name", NULL);
|
2005-12-05 05:16:07 -06:00
|
|
|
virDomainPtr dom;
|
2005-12-08 04:23:34 -06:00
|
|
|
|
|
|
|
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
if (!name)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
dom = virDomainLookupByName(ctl->conn, name);
|
|
|
|
if (dom) {
|
|
|
|
vshPrint(ctl, VSH_MESG, "%s\n", virDomainGetID(dom));
|
|
|
|
/*TODO: virDomainFree(dom); */
|
|
|
|
} else {
|
|
|
|
vshError(ctl, FALSE, "failed to get domain '%s'", name);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2005-12-08 07:26:52 -06:00
|
|
|
/*
|
|
|
|
* "version" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_version[] = {
|
|
|
|
{ "syntax", "version" },
|
|
|
|
{ "help", "show versions" },
|
|
|
|
{ "desc", "Display the version informations available" },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdVersion(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
unsigned long hvVersion;
|
|
|
|
const char *hvType;
|
|
|
|
|
|
|
|
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
hvType = virConnectGetType(ctl->conn);
|
|
|
|
if (hvType == NULL) {
|
|
|
|
vshError(ctl, FALSE, "Failed to get hypervisor type\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
hvVersion = virConnectGetVersion(ctl->conn);
|
|
|
|
if (hvVersion < 0) {
|
|
|
|
vshError(ctl, FALSE, "failed get hypervisor version");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (hvVersion == 0) {
|
|
|
|
vshPrint(ctl, VSH_MESG,
|
|
|
|
"Cannot extract running %s hypervisor version\n",
|
|
|
|
hvType);
|
|
|
|
} else {
|
|
|
|
unsigned int major = hvVersion / 1000000;
|
|
|
|
unsigned int minor;
|
|
|
|
unsigned int rel;
|
|
|
|
|
|
|
|
hvVersion %= 1000000;
|
|
|
|
minor = hvVersion / 1000000;
|
|
|
|
rel = hvVersion % 1000000;
|
|
|
|
|
|
|
|
vshPrint(ctl, VSH_MESG, "Running hypervisor: %s %d.%d.%d\n", hvType,
|
|
|
|
major, minor, rel);
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2005-12-08 04:23:34 -06:00
|
|
|
/*
|
|
|
|
* "quit" command
|
|
|
|
*/
|
|
|
|
static vshCmdInfo info_quit[] = {
|
|
|
|
{ "syntax", "quit" },
|
|
|
|
{ "help", "quit this interactive terminal" },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmdQuit(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
ctl->imode = FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Commands
|
|
|
|
*/
|
|
|
|
static vshCmdDef commands[] = {
|
|
|
|
{ "connect", cmdConnect, opts_connect, info_connect },
|
|
|
|
{ "dinfo", cmdDinfo, opts_dinfo, info_dinfo },
|
|
|
|
{ "dstate", cmdDstate, opts_dstate, info_dstate },
|
|
|
|
{ "help", cmdHelp, opts_help, info_help },
|
|
|
|
{ "idof", cmdIdof, opts_idof, info_idof },
|
|
|
|
{ "list", cmdList, NULL, info_list },
|
|
|
|
{ "nameof", cmdNameof, opts_nameof, info_nameof },
|
2005-12-08 07:26:52 -06:00
|
|
|
{ "version", cmdVersion, NULL, info_version },
|
2005-12-08 04:23:34 -06:00
|
|
|
{ "quit", cmdQuit, NULL, info_quit },
|
|
|
|
{ NULL, NULL, NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ---------------
|
|
|
|
* Utils for work with command definition
|
|
|
|
* ---------------
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
vshCmddefGetInfo(vshCmdDef *cmd, char *name) {
|
|
|
|
vshCmdInfo *info;
|
|
|
|
|
|
|
|
for (info = cmd->info; info && info->name; info++) {
|
|
|
|
if (strcmp(info->name, name)==0)
|
|
|
|
return info->data;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static vshCmdOptDef *
|
|
|
|
vshCmddefGetOption(vshCmdDef *cmd, char *name) {
|
|
|
|
vshCmdOptDef *opt;
|
|
|
|
|
|
|
|
for (opt = cmd->opts; opt && opt->name; opt++)
|
|
|
|
if (strcmp(opt->name, name)==0)
|
|
|
|
return opt;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static vshCmdOptDef *
|
|
|
|
vshCmddefGetData(vshCmdDef *cmd) {
|
|
|
|
vshCmdOptDef *opt;
|
|
|
|
|
|
|
|
for (opt = cmd->opts; opt && opt->name; opt++)
|
|
|
|
if (opt->type==VSH_OT_DATA)
|
|
|
|
return opt;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static vshCmdDef *
|
|
|
|
vshCmddefSearch(char *cmdname) {
|
|
|
|
vshCmdDef *c;
|
|
|
|
|
|
|
|
for (c = commands; c->name; c++)
|
|
|
|
if (strcmp(c->name, cmdname)==0)
|
|
|
|
return c;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vshCmddefHelp(vshControl *ctl, char *cmdname, int withprog) {
|
|
|
|
vshCmdDef *def = vshCmddefSearch(cmdname);
|
|
|
|
|
|
|
|
if (!def) {
|
|
|
|
vshError(ctl, FALSE, "command '%s' doesn't exist", cmdname);
|
|
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
vshCmdOptDef *opt;
|
|
|
|
char *desc = vshCmddefGetInfo(def, "desc");
|
|
|
|
char *help = vshCmddefGetInfo(def, "help");
|
|
|
|
char *syntax = vshCmddefGetInfo(def, "syntax");
|
|
|
|
|
|
|
|
fputs(" NAME\n", stdout);
|
|
|
|
fprintf(stdout, " %s - %s\n", def->name, help);
|
|
|
|
|
|
|
|
if (syntax) {
|
|
|
|
fputs("\n SYNOPSIS\n", stdout);
|
|
|
|
if (!withprog)
|
|
|
|
fprintf(stdout, " %s\n", syntax);
|
|
|
|
else
|
|
|
|
fprintf(stdout, " %s %s\n", progname, syntax);
|
|
|
|
}
|
|
|
|
if (desc) {
|
|
|
|
fputs("\n DESCRIPTION\n", stdout);
|
|
|
|
fprintf(stdout, " %s\n", desc);
|
|
|
|
}
|
|
|
|
if (def->opts) {
|
|
|
|
fputs("\n OPTIONS\n", stdout);
|
|
|
|
for (opt=def->opts; opt->name; opt++) {
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if (opt->type==VSH_OT_BOOL)
|
|
|
|
snprintf(buf, sizeof(buf), "--%s", opt->name);
|
|
|
|
else if (opt->type==VSH_OT_INT)
|
|
|
|
snprintf(buf, sizeof(buf), "--%s <number>", opt->name);
|
|
|
|
else if (opt->type==VSH_OT_STRING)
|
|
|
|
snprintf(buf, sizeof(buf), "--%s <string>", opt->name);
|
|
|
|
else if (opt->type==VSH_OT_DATA)
|
|
|
|
snprintf(buf, sizeof(buf), "<%s>", opt->name);
|
|
|
|
|
|
|
|
fprintf(stdout, " %-15s %s\n", buf, opt->help);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fputc('\n', stdout);
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------
|
|
|
|
* Utils for work with runtime commands data
|
|
|
|
* ---------------
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
vshCommandOptFree(vshCmdOpt *arg) {
|
|
|
|
vshCmdOpt *a = arg;
|
|
|
|
|
|
|
|
while(a) {
|
|
|
|
vshCmdOpt *tmp = a;
|
|
|
|
a = a->next;
|
|
|
|
|
|
|
|
if (tmp->data)
|
|
|
|
free(tmp->data);
|
|
|
|
free(tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vshCommandFree(vshCmd *cmd) {
|
|
|
|
vshCmd *c = cmd;
|
|
|
|
|
|
|
|
while(c) {
|
|
|
|
vshCmd *tmp = c;
|
|
|
|
c = c->next;
|
|
|
|
|
|
|
|
if (tmp->opts)
|
|
|
|
vshCommandOptFree(tmp->opts);
|
|
|
|
free(tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns option by name
|
|
|
|
*/
|
|
|
|
static vshCmdOpt *
|
|
|
|
vshCommandOpt(vshCmd *cmd, char *name) {
|
|
|
|
vshCmdOpt *opt = cmd->opts;
|
|
|
|
|
|
|
|
while(opt) {
|
|
|
|
if (opt->def && strcmp(opt->def->name, name)==0)
|
|
|
|
return opt;
|
|
|
|
opt = opt->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns option as INT
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vshCommandOptInt(vshCmd *cmd, char *name, int *found) {
|
|
|
|
vshCmdOpt *arg = vshCommandOpt(cmd, name);
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
if (arg)
|
|
|
|
res = atoi(arg->data);
|
|
|
|
if (found)
|
|
|
|
*found = arg ? TRUE : FALSE;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns option as STRING
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
vshCommandOptString(vshCmd *cmd, char *name, int *found) {
|
|
|
|
vshCmdOpt *arg = vshCommandOpt(cmd, name);
|
|
|
|
if (found)
|
|
|
|
*found = arg ? TRUE : FALSE;
|
|
|
|
return arg ? arg->data : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns TRUE/FALSE if the option exists
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vshCommandOptBool(vshCmd *cmd, char *name) {
|
|
|
|
return vshCommandOpt(cmd, name) ? TRUE : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Executes command(s) and returns return code from last command
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vshCommandRun(vshControl *ctl, vshCmd *cmd) {
|
|
|
|
int ret = TRUE;
|
|
|
|
|
|
|
|
while(cmd) {
|
|
|
|
struct timeval before, after;
|
|
|
|
|
|
|
|
if (ctl->timing)
|
|
|
|
GETTIMEOFDAY(&before);
|
|
|
|
|
|
|
|
ret = cmd->def->handler(ctl, cmd);
|
|
|
|
|
|
|
|
if (ctl->timing)
|
|
|
|
GETTIMEOFDAY(&after);
|
|
|
|
|
|
|
|
if (strcmp(cmd->def->name, "quit")==0) /* hack ... */
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (ctl->timing)
|
|
|
|
vshPrint(ctl, VSH_MESG, "\n(Time: %.3f ms)\n\n", DIFF_MSEC(&after, &before));
|
|
|
|
else
|
|
|
|
vshPrint(ctl, VSH_FOOTER, "\n");
|
|
|
|
cmd = cmd->next;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------
|
|
|
|
* Command string parsing
|
|
|
|
* ---------------
|
|
|
|
*/
|
|
|
|
#define VSH_TK_ERROR -1
|
|
|
|
#define VSH_TK_NONE 0
|
|
|
|
#define VSH_TK_OPTION 1
|
|
|
|
#define VSH_TK_DATA 2
|
|
|
|
#define VSH_TK_END 3
|
|
|
|
|
|
|
|
static int
|
|
|
|
vshCommandGetToken(vshControl *ctl, char *str, char **end, char **res) {
|
|
|
|
int tk = VSH_TK_NONE;
|
|
|
|
int quote = FALSE;
|
|
|
|
int sz = 0;
|
|
|
|
char *p = str;
|
|
|
|
char *tkstr = NULL;
|
|
|
|
|
|
|
|
*end = NULL;
|
|
|
|
|
|
|
|
while(p && *p && isblank((unsigned char) *p))
|
|
|
|
p++;
|
2005-11-10 10:12:31 -06:00
|
|
|
|
2005-12-08 04:23:34 -06:00
|
|
|
if (p==NULL || *p=='\0')
|
|
|
|
return VSH_TK_END;
|
|
|
|
if (*p==';') {
|
|
|
|
*end = ++p; /* = \0 or begi of next command */
|
|
|
|
return VSH_TK_END;
|
|
|
|
}
|
|
|
|
while(*p) {
|
|
|
|
/* end of token is blank space or ';' */
|
|
|
|
if ((quote==FALSE && isblank((unsigned char) *p)) || *p==';')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (tk==VSH_TK_NONE) {
|
|
|
|
if (*p=='-' && *(p+1)=='-' && *(p+2) && isalnum((unsigned char) *(p+2))) {
|
|
|
|
tk = VSH_TK_OPTION;
|
|
|
|
p+=2;
|
|
|
|
} else {
|
|
|
|
tk = VSH_TK_DATA;
|
|
|
|
if (*p=='"') {
|
|
|
|
quote = TRUE;
|
|
|
|
p++;
|
|
|
|
} else {
|
|
|
|
quote = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tkstr = p; /* begin of token */
|
|
|
|
} else if (quote && *p=='"') {
|
|
|
|
quote = FALSE;
|
|
|
|
p++;
|
|
|
|
break; /* end of "..." token */
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
sz++;
|
|
|
|
}
|
|
|
|
if (quote) {
|
|
|
|
vshError(ctl, FALSE, "missing \"");
|
|
|
|
return VSH_TK_ERROR;
|
|
|
|
}
|
|
|
|
if (tkstr==NULL || *tkstr=='\0' || p==NULL)
|
|
|
|
return VSH_TK_END;
|
|
|
|
if (sz==0)
|
|
|
|
return VSH_TK_END;
|
|
|
|
|
|
|
|
*res = malloc(sz+1);
|
|
|
|
memcpy(*res, tkstr, sz);
|
|
|
|
*(*res+sz) = '\0';
|
|
|
|
|
|
|
|
*end = p;
|
|
|
|
return tk;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vshCommandParse(vshControl *ctl, char *cmdstr) {
|
|
|
|
char *str;
|
|
|
|
char *tkdata = NULL;
|
|
|
|
vshCmd *clast = NULL;
|
|
|
|
vshCmdOpt *first = NULL;
|
|
|
|
|
|
|
|
if (ctl->cmd) {
|
|
|
|
vshCommandFree(ctl->cmd);
|
|
|
|
ctl->cmd = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmdstr==NULL || *cmdstr=='\0')
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
str = cmdstr;
|
|
|
|
while(str && *str)
|
|
|
|
{
|
|
|
|
vshCmdOpt *last = NULL;
|
|
|
|
vshCmdDef *cmd = NULL;
|
|
|
|
int tk = VSH_TK_NONE;
|
|
|
|
|
|
|
|
first = NULL;
|
|
|
|
|
|
|
|
while (tk!=VSH_TK_END) {
|
|
|
|
char *end = NULL;
|
|
|
|
vshCmdOptDef *opt = NULL;
|
|
|
|
|
|
|
|
tkdata = NULL;
|
|
|
|
|
|
|
|
/* get token */
|
|
|
|
tk = vshCommandGetToken(ctl, str, &end, &tkdata);
|
|
|
|
|
|
|
|
str = end;
|
|
|
|
|
|
|
|
if (tk==VSH_TK_END)
|
|
|
|
break;
|
|
|
|
if (tk==VSH_TK_ERROR)
|
|
|
|
goto syntaxError;
|
|
|
|
|
|
|
|
if (cmd==NULL) {
|
|
|
|
/* first token must be command name */
|
|
|
|
if (tk!=VSH_TK_DATA) {
|
|
|
|
vshError(ctl, FALSE,
|
|
|
|
"unexpected token (command name): '%s'",
|
|
|
|
tkdata);
|
|
|
|
goto syntaxError;
|
|
|
|
}
|
|
|
|
if (!(cmd = vshCmddefSearch(tkdata))) {
|
|
|
|
vshError(ctl, FALSE,
|
|
|
|
"unknown command: '%s'", tkdata);
|
|
|
|
goto syntaxError; /* ... or ignore this command only? */
|
|
|
|
}
|
|
|
|
free(tkdata);
|
|
|
|
} else if (tk==VSH_TK_OPTION) {
|
|
|
|
if (!(opt = vshCmddefGetOption(cmd, tkdata))) {
|
|
|
|
vshError(ctl, FALSE,
|
|
|
|
"command '%s' doesn't support option --%s",
|
|
|
|
cmd->name, tkdata);
|
|
|
|
goto syntaxError;
|
|
|
|
}
|
|
|
|
free(tkdata); /* option name */
|
|
|
|
tkdata = NULL;
|
|
|
|
|
|
|
|
if (opt->type != VSH_OT_BOOL) {
|
|
|
|
/* option data */
|
|
|
|
tk = vshCommandGetToken(ctl, str, &end, &tkdata);
|
|
|
|
str = end;
|
|
|
|
if (tk==VSH_TK_ERROR)
|
|
|
|
goto syntaxError;
|
|
|
|
if (tk!=VSH_TK_DATA) {
|
|
|
|
vshError(ctl, FALSE,
|
|
|
|
"expected syntax: --%s <%s>",
|
|
|
|
opt->name,
|
|
|
|
opt->type==VSH_OT_INT ? "number" : "string");
|
|
|
|
goto syntaxError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (tk==VSH_TK_DATA) {
|
|
|
|
if (!(opt = vshCmddefGetData(cmd))) {
|
|
|
|
vshError(ctl, FALSE,
|
|
|
|
"unexpected data '%s'",
|
|
|
|
tkdata);
|
|
|
|
goto syntaxError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (opt) {
|
|
|
|
/* save option */
|
|
|
|
vshCmdOpt *arg = malloc(sizeof(vshCmdOpt));
|
|
|
|
|
|
|
|
arg->def = opt;
|
|
|
|
arg->data = tkdata;
|
|
|
|
arg->next = NULL;
|
|
|
|
tkdata = NULL;
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
first = arg;
|
|
|
|
if (last)
|
|
|
|
last->next = arg;
|
|
|
|
last = arg;
|
|
|
|
|
|
|
|
vshPrint(ctl, VSH_DEBUG4, "%s: %s(%s): %s\n",
|
|
|
|
cmd->name,
|
|
|
|
opt->name,
|
|
|
|
tk==VSH_TK_OPTION ? "OPTION" : "DATA",
|
|
|
|
arg->data);
|
|
|
|
}
|
|
|
|
if (!str)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* commad parsed -- allocate new struct for the command */
|
|
|
|
if (cmd) {
|
|
|
|
vshCmd *c = malloc(sizeof(vshCmd));
|
|
|
|
c->opts = first;
|
|
|
|
c->def = cmd;
|
|
|
|
c->next = NULL;
|
|
|
|
|
|
|
|
if (!ctl->cmd)
|
|
|
|
ctl->cmd = c;
|
|
|
|
if (clast)
|
|
|
|
clast->next = c;
|
|
|
|
clast = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
syntaxError:
|
|
|
|
if (ctl->cmd)
|
|
|
|
vshCommandFree(ctl->cmd);
|
|
|
|
if (first)
|
|
|
|
vshCommandOptFree(first);
|
|
|
|
if (tkdata)
|
|
|
|
free(tkdata);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------
|
|
|
|
* Misc utils
|
|
|
|
* ---------------
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
vshDomainStateToString(int state) {
|
|
|
|
switch (state) {
|
|
|
|
case VIR_DOMAIN_RUNNING:
|
|
|
|
return "running ";
|
|
|
|
case VIR_DOMAIN_BLOCKED:
|
|
|
|
return "blocked ";
|
|
|
|
case VIR_DOMAIN_PAUSED:
|
|
|
|
return "paused ";
|
|
|
|
case VIR_DOMAIN_SHUTDOWN:
|
|
|
|
return "in shutdown";
|
|
|
|
case VIR_DOMAIN_SHUTOFF:
|
|
|
|
return "shut off";
|
|
|
|
default:
|
|
|
|
return "no state"; /* = dom0 state */
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vshConnectionUsability(vshControl *ctl, virConnectPtr conn, int showerror) {
|
|
|
|
/* TODO: use something like virConnectionState() to
|
|
|
|
* check usability of the connection
|
|
|
|
*/
|
|
|
|
if (!conn) {
|
|
|
|
if (showerror)
|
|
|
|
vshError(ctl, FALSE, "no valid connection.");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vshWantedDebug(vshOutType type, int mode) {
|
|
|
|
switch(type) {
|
|
|
|
case VSH_DEBUG5:
|
|
|
|
if (mode < 5)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
case VSH_DEBUG4:
|
|
|
|
if (mode < 4)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
case VSH_DEBUG3:
|
|
|
|
if (mode < 3)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
case VSH_DEBUG2:
|
|
|
|
if (mode < 2)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
case VSH_DEBUG1:
|
|
|
|
if (mode < 1)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
default:
|
|
|
|
/* it's right, all others types have to pass */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vshPrint(vshControl *ctl, vshOutType type, char *format, ...) {
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (ctl->quiet==TRUE && (type==VSH_HEADER || type==VSH_FOOTER))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!vshWantedDebug(type, ctl->debug))
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vshError(vshControl *ctl, int doexit, char *format, ...) {
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (doexit)
|
|
|
|
fprintf(stderr, "%s: error: ", progname);
|
|
|
|
else
|
|
|
|
fputs("error: ", stderr);
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
fputc('\n', stderr);
|
|
|
|
|
|
|
|
if (doexit) {
|
|
|
|
vshDeinit(ctl);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize vistsh
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vshInit(vshControl *ctl) {
|
|
|
|
if (ctl->conn)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
ctl->uid = getuid();
|
|
|
|
|
|
|
|
/* basic connection to hypervisor */
|
|
|
|
if (ctl->uid == 0)
|
|
|
|
ctl->conn = virConnectOpen(NULL);
|
|
|
|
else
|
|
|
|
ctl->conn = virConnectOpenReadOnly(NULL);
|
|
|
|
|
|
|
|
if (!ctl->conn)
|
|
|
|
vshError(ctl, TRUE, "failed to connect to the hypervisor");
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -----------------
|
|
|
|
* 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 list_index, len;
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
/* If this is a new word to complete, initialize now. This
|
|
|
|
* includes saving the length of TEXT for efficiency, and
|
|
|
|
* initializing the index variable to 0.
|
|
|
|
*/
|
|
|
|
if (!state) {
|
|
|
|
list_index = 0;
|
|
|
|
len = strlen (text);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the next name which partially matches from the
|
|
|
|
* command list.
|
|
|
|
*/
|
|
|
|
while (name = commands[list_index].name) {
|
|
|
|
list_index++;
|
|
|
|
if (strncmp (name, text, len) == 0)
|
|
|
|
return strdup(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If no names matched, then return NULL. */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
vshReadlineOptionsGenerator(const char *text, int state) {
|
|
|
|
static int list_index, len;
|
|
|
|
static vshCmdDef *cmd = NULL;
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
if (!state) {
|
|
|
|
/* determine command name */
|
|
|
|
char *p;
|
|
|
|
char *cmdname;
|
|
|
|
|
|
|
|
if (!(p = strchr(rl_line_buffer, ' ')))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
cmdname = calloc((p - rl_line_buffer)+ 1, 1);
|
|
|
|
memcpy(cmdname, rl_line_buffer, p-rl_line_buffer);
|
|
|
|
|
|
|
|
cmd = vshCmddefSearch(cmdname);
|
|
|
|
list_index = 0;
|
|
|
|
len = strlen (text);
|
|
|
|
free(cmdname);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
while (name = cmd->opts[list_index].name) {
|
|
|
|
vshCmdOptDef *opt = &cmd->opts[list_index];
|
|
|
|
char *res;
|
|
|
|
list_index++;
|
|
|
|
|
|
|
|
if (cmd->opts[list_index].type == VSH_OT_DATA)
|
|
|
|
/* ignore non --option */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (len > 2) {
|
|
|
|
if (strncmp (name, text+2, len-2))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
res = malloc(strlen(name)+3);
|
|
|
|
sprintf(res, "--%s", name);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If no names matched, then return NULL. */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char **
|
|
|
|
vshReadlineCompletion(const char *text, int start, int end) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
vshReadlineInit(vshControl *ctl) {
|
|
|
|
/* Allow conditional parsing of the ~/.inputrc file. */
|
|
|
|
rl_readline_name = "virsh";
|
|
|
|
|
|
|
|
/* Tell the completer that we want a crack first. */
|
|
|
|
rl_attempted_completion_function = vshReadlineCompletion;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deinitliaze virsh
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vshDeinit(vshControl *ctl) {
|
|
|
|
if (ctl->conn) {
|
|
|
|
if (virConnectClose(ctl->conn)!=0) {
|
|
|
|
ctl->conn = NULL; /* prevent recursive call from vshError() */
|
|
|
|
vshError(ctl, TRUE, "failed to disconnect from the hypervisor");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print usage
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
vshUsage(vshControl *ctl, char *cmdname) {
|
|
|
|
vshCmdDef *cmd;
|
|
|
|
|
|
|
|
/* global help */
|
|
|
|
if (!cmdname) {
|
|
|
|
fprintf(stdout, "\n%s [options] [commands]\n\n"
|
|
|
|
" options:\n"
|
|
|
|
" -d | --debug <num> debug level [0-5]\n"
|
|
|
|
" -h | --help this help\n"
|
|
|
|
" -q | --quiet quiet mode\n"
|
|
|
|
" -t | --timing print timing information\n"
|
|
|
|
" -v | --version program version\n\n"
|
|
|
|
" commands (non interactive mode):\n", progname);
|
|
|
|
|
|
|
|
for(cmd = commands; cmd->name; cmd++)
|
|
|
|
fprintf(stdout,
|
|
|
|
" %-15s %s\n", cmd->name, vshCmddefGetInfo(cmd, "help"));
|
|
|
|
|
|
|
|
fprintf(stdout, "\n (specify --help <command> for details about the command)\n\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!vshCmddefHelp(ctl, cmdname, TRUE))
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* argv[]: virsh [options] [command]
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vshParseArgv(vshControl *ctl, int argc, char **argv) {
|
|
|
|
char *last = NULL;
|
|
|
|
int i, end = 0, help = 0;
|
|
|
|
int arg, idx=0;
|
|
|
|
struct option opt[] = {
|
|
|
|
{ "debug", 1, 0, 'd' },
|
|
|
|
{ "help", 0, 0, 'h' },
|
|
|
|
{ "quiet", 0, 0, 'q' },
|
|
|
|
{ "timing", 0, 0, 't' },
|
|
|
|
{ "version", 0, 0, 'v' },
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* look for begin of command, for example:
|
|
|
|
* ./virsh --debug 5 -q command --cmdoption
|
|
|
|
* <--- ^ --->
|
|
|
|
* getopt() stuff | command suff
|
|
|
|
*/
|
|
|
|
for(i=1; i < argc; i++) {
|
|
|
|
if (*argv[i] != '-') {
|
|
|
|
int valid = FALSE;
|
|
|
|
|
|
|
|
/* non "--option" argv, is it command? */
|
|
|
|
if (last) {
|
|
|
|
struct option *o;
|
|
|
|
int sz = strlen(last);
|
|
|
|
|
|
|
|
for(o=opt; o->name; o++) {
|
|
|
|
if (sz==2 && *(last+1)==o->val)
|
|
|
|
/* valid virsh short option */
|
|
|
|
valid = TRUE;
|
|
|
|
else if (sz > 2 && strcmp(o->name, last+2)==0)
|
|
|
|
/* valid virsh long option */
|
|
|
|
valid = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!valid) {
|
|
|
|
end = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last = argv[i];
|
|
|
|
}
|
|
|
|
end = end ? : argc;
|
|
|
|
|
|
|
|
/* standard (non-command) options */
|
|
|
|
while((arg = getopt_long(end, argv, "d:hqtv", opt, &idx)) != -1) {
|
|
|
|
switch(arg) {
|
|
|
|
case 'd':
|
|
|
|
ctl->debug = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
help = 1;
|
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
ctl->quiet = TRUE;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
ctl->timing = TRUE;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
fprintf(stdout, "%s\n", VERSION);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
default:
|
|
|
|
vshError(ctl, TRUE, "unsupported option '-%c'. See --help.", arg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (help) {
|
|
|
|
/* global or command specific help */
|
|
|
|
vshUsage(ctl, argc > end ? argv[end] : NULL);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > end) {
|
|
|
|
/* parse command */
|
|
|
|
char *cmdstr;
|
|
|
|
int sz=0, i, ret;
|
|
|
|
|
|
|
|
ctl->imode = FALSE;
|
|
|
|
|
|
|
|
for (i=end; i < argc; i++)
|
|
|
|
sz += strlen(argv[i]) + 1; /* +1 is for blank space between items */
|
|
|
|
|
|
|
|
cmdstr = calloc(sz+1, 1);
|
|
|
|
|
|
|
|
for (i=end; i < argc; i++) {
|
|
|
|
strncat(cmdstr, argv[i], sz);
|
|
|
|
sz -= strlen(argv[i]);
|
|
|
|
strncat(cmdstr, " ", sz--);
|
|
|
|
}
|
|
|
|
vshPrint(ctl, VSH_DEBUG2, "command: \"%s\"\n", cmdstr);
|
|
|
|
ret = vshCommandParse(ctl, cmdstr);
|
|
|
|
|
|
|
|
free(cmdstr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv) {
|
|
|
|
vshControl _ctl, *ctl=&_ctl;
|
|
|
|
int ret = TRUE;
|
|
|
|
|
|
|
|
if (!(progname=strrchr(argv[0], '/')))
|
|
|
|
progname = argv[0];
|
|
|
|
else
|
|
|
|
progname++;
|
|
|
|
|
|
|
|
memset(ctl, 0, sizeof(vshControl));
|
|
|
|
ctl->imode = TRUE; /* default is interactive mode */
|
|
|
|
|
|
|
|
if (!vshParseArgv(ctl, argc, argv))
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
if (!vshInit(ctl))
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
if (!ctl->imode) {
|
|
|
|
ret = vshCommandRun(ctl, ctl->cmd);
|
2005-12-01 10:35:42 -06:00
|
|
|
} else {
|
2005-12-08 04:23:34 -06:00
|
|
|
/* interactive mode */
|
|
|
|
if (!ctl->quiet) {
|
|
|
|
vshPrint(ctl, VSH_MESG, "Welcome to %s, the virtualization interactive terminal.\n\n",
|
|
|
|
progname);
|
|
|
|
vshPrint(ctl, VSH_MESG, "Type: 'help' for help with commands\n"
|
|
|
|
" 'quit' to quit\n\n");
|
|
|
|
}
|
|
|
|
vshReadlineInit(ctl);
|
|
|
|
do {
|
|
|
|
ctl->cmdstr = readline(ctl->uid==0 ? VSH_PROMPT_RW : VSH_PROMPT_RO);
|
|
|
|
if (ctl->cmdstr==NULL)
|
|
|
|
break; /* EOF */
|
|
|
|
if (*ctl->cmdstr) {
|
|
|
|
add_history(ctl->cmdstr);
|
|
|
|
if (vshCommandParse(ctl, ctl->cmdstr))
|
|
|
|
vshCommandRun(ctl, ctl->cmd);
|
|
|
|
}
|
|
|
|
free(ctl->cmdstr);
|
|
|
|
ctl->cmdstr = NULL;
|
|
|
|
} while(ctl->imode);
|
|
|
|
|
|
|
|
if (ctl->cmdstr==NULL)
|
|
|
|
fputc('\n', stdout); /* line break after alone prompt */
|
|
|
|
}
|
|
|
|
|
|
|
|
vshDeinit(ctl);
|
|
|
|
exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
|
2005-11-10 10:12:31 -06:00
|
|
|
}
|
2005-12-08 04:23:34 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* vim: set tabstop=4:
|
|
|
|
* vim: set shiftwidth=4:
|
|
|
|
* vim: set expandtab:
|
|
|
|
*/
|
|
|
|
|