virsh.c 193.1 KB
Newer Older
1
/*
2
 * virsh.c: a shell to exercise the libvirt API
3
 *
4
 * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
5 6 7 8
 *
 * See COPYING.LIB for the License of this software
 *
 * Daniel Veillard <veillard@redhat.com>
K
Karel Zak 已提交
9
 * Karel Zak <kzak@redhat.com>
K
Karel Zak 已提交
10
 * Daniel P. Berrange <berrange@redhat.com>
11 12
 */

13
#include <config.h>
14

15
#include <stdio.h>
K
Karel Zak 已提交
16 17 18
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
19
#include <unistd.h>
20
#include <errno.h>
K
Karel Zak 已提交
21
#include <getopt.h>
22
#include <sys/types.h>
K
Karel Zak 已提交
23
#include <sys/time.h>
E
Eric Blake 已提交
24
#include <sys/wait.h>
J
Jim Meyering 已提交
25
#include "c-ctype.h"
26
#include <fcntl.h>
27
#include <locale.h>
28
#include <time.h>
29
#include <limits.h>
30
#include <assert.h>
31
#include <sys/stat.h>
32
#include <inttypes.h>
33
#include <signal.h>
34
#include <poll.h>
E
Eric Blake 已提交
35
#include <strings.h>
36
#include <termios.h>
K
Karel Zak 已提交
37

38 39 40
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
41
#include <libxml/xmlsave.h>
42

43
#ifdef HAVE_READLINE_READLINE_H
44 45
# include <readline/readline.h>
# include <readline/history.h>
46
#endif
K
Karel Zak 已提交
47

48
#include "internal.h"
49
#include "virterror_internal.h"
50
#include "base64.h"
51
#include "buf.h"
52
#include "console.h"
53
#include "util.h"
54
#include "memory.h"
55
#include "xml.h"
56
#include "libvirt/libvirt-qemu.h"
E
Eric Blake 已提交
57
#include "virfile.h"
58
#include "event_poll.h"
59
#include "configmake.h"
60
#include "threads.h"
E
Eric Blake 已提交
61
#include "command.h"
62
#include "virkeycode.h"
63
#include "virnetdevbandwidth.h"
64
#include "util/bitmap.h"
H
Hu Tao 已提交
65
#include "conf/domain_conf.h"
66
#include "virtypedparam.h"
67
#include "conf/virdomainlist.h"
K
Karel Zak 已提交
68 69 70

static char *progname;

71 72
#define VIRSH_MAX_XML_FILE 10*1024*1024

K
Karel Zak 已提交
73 74 75
#define VSH_PROMPT_RW    "virsh # "
#define VSH_PROMPT_RO    "virsh > "

76 77
#define VIR_FROM_THIS VIR_FROM_NONE

K
Karel Zak 已提交
78 79 80 81 82
#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)

83 84 85
/* Default escape char Ctrl-] as per telnet */
#define CTRL_CLOSE_BRACKET "^]"

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
/**
 * 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:
 *
J
Jim Meyering 已提交
103
 * Indicates the level of a log message
104 105 106 107 108 109 110 111 112
 */
typedef enum {
    VSH_ERR_DEBUG = 0,
    VSH_ERR_INFO,
    VSH_ERR_NOTICE,
    VSH_ERR_WARNING,
    VSH_ERR_ERROR
} vshErrorLevel;

J
Jiri Denemark 已提交
113 114
#define VSH_DEBUG_DEFAULT VSH_ERR_ERROR

K
Karel Zak 已提交
115 116 117 118 119
/*
 * virsh command line grammar:
 *
 *    command_line    =     <command>\n | <command>; <command>; ...
 *
E
Eric Blake 已提交
120
 *    command         =    <keyword> <option> [--] <data>
K
Karel Zak 已提交
121 122 123 124 125
 *
 *    option          =     <bool_option> | <int_option> | <string_option>
 *    data            =     <string>
 *
 *    bool_option     =     --optionname
E
Eric Blake 已提交
126 127
 *    int_option      =     --optionname <number> | --optionname=<number>
 *    string_option   =     --optionname <string> | --optionname=<string>
128
 *
E
Eric Blake 已提交
129
 *    keyword         =     [a-zA-Z][a-zA-Z-]*
130
 *    number          =     [0-9]+
E
Eric Blake 已提交
131
 *    string          =     ('[^']*'|"([^\\"]|\\.)*"|([^ \t\n\\'"]|\\.))+
K
Karel Zak 已提交
132 133 134 135
 *
 */

/*
136
 * vshCmdOptType - command option type
137
 */
K
Karel Zak 已提交
138
typedef enum {
139 140 141
    VSH_OT_BOOL,     /* optional boolean option */
    VSH_OT_STRING,   /* optional string option */
    VSH_OT_INT,      /* optional or mandatory int option */
142
    VSH_OT_DATA,     /* string data (as non-option) */
E
Eric Blake 已提交
143 144
    VSH_OT_ARGV,     /* remaining arguments */
    VSH_OT_ALIAS,    /* alternate spelling for a later argument */
K
Karel Zak 已提交
145 146
} vshCmdOptType;

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
/*
 * Command group types
 */
#define VSH_CMD_GRP_DOM_MANAGEMENT   "Domain Management"
#define VSH_CMD_GRP_DOM_MONITORING   "Domain Monitoring"
#define VSH_CMD_GRP_STORAGE_POOL     "Storage Pool"
#define VSH_CMD_GRP_STORAGE_VOL      "Storage Volume"
#define VSH_CMD_GRP_NETWORK          "Networking"
#define VSH_CMD_GRP_NODEDEV          "Node Device"
#define VSH_CMD_GRP_IFACE            "Interface"
#define VSH_CMD_GRP_NWFILTER         "Network Filter"
#define VSH_CMD_GRP_SECRET           "Secret"
#define VSH_CMD_GRP_SNAPSHOT         "Snapshot"
#define VSH_CMD_GRP_HOST_AND_HV      "Host and Hypervisor"
#define VSH_CMD_GRP_VIRSH            "Virsh itself"

K
Karel Zak 已提交
163 164 165
/*
 * Command Option Flags
 */
E
Eric Blake 已提交
166 167 168 169
enum {
    VSH_OFLAG_NONE     = 0,        /* without flags */
    VSH_OFLAG_REQ      = (1 << 0), /* option required */
    VSH_OFLAG_EMPTY_OK = (1 << 1), /* empty string option allowed */
L
Lai Jiangshan 已提交
170
    VSH_OFLAG_REQ_OPT  = (1 << 2), /* --optionname required */
E
Eric Blake 已提交
171
};
K
Karel Zak 已提交
172 173 174 175 176 177

/* dummy */
typedef struct __vshControl vshControl;
typedef struct __vshCmd vshCmd;

/*
E
Eric Blake 已提交
178 179 180 181 182
 * vshCmdInfo -- name/value pair for information about command
 *
 * Commands should have at least the following names:
 * "name" - command name
 * "desc" - description of command, or empty string
K
Karel Zak 已提交
183
 */
184
typedef struct {
E
Eric Blake 已提交
185 186
    const char *name;           /* name of information, or NULL for list end */
    const char *data;           /* non-NULL information */
K
Karel Zak 已提交
187 188 189 190 191
} vshCmdInfo;

/*
 * vshCmdOptDef - command option definition
 */
192
typedef struct {
E
Eric Blake 已提交
193
    const char *name;           /* the name of option, or NULL for list end */
194
    vshCmdOptType type;         /* option type */
E
Eric Blake 已提交
195
    unsigned int flags;         /* flags */
E
Eric Blake 已提交
196 197
    const char *help;           /* non-NULL help string; or for VSH_OT_ALIAS
                                 * the name of a later public option */
K
Karel Zak 已提交
198 199 200 201
} vshCmdOptDef;

/*
 * vshCmdOpt - command options
E
Eric Blake 已提交
202 203 204
 *
 * After parsing a command, all arguments to the command have been
 * collected into a list of these objects.
K
Karel Zak 已提交
205 206
 */
typedef struct vshCmdOpt {
E
Eric Blake 已提交
207 208
    const vshCmdOptDef *def;    /* non-NULL pointer to option definition */
    char *data;                 /* allocated data, or NULL for bool option */
209
    struct vshCmdOpt *next;
K
Karel Zak 已提交
210 211
} vshCmdOpt;

212 213 214 215 216
/*
 * Command Usage Flags
 */
enum {
    VSH_CMD_FLAG_NOCONNECT = (1 << 0),  /* no prior connection needed */
217
    VSH_CMD_FLAG_ALIAS     = (1 << 1),  /* command is an alias */
218 219
};

K
Karel Zak 已提交
220 221 222
/*
 * vshCmdDef - command definition
 */
223
typedef struct {
E
Eric Blake 已提交
224
    const char *name;           /* name of command, or NULL for list end */
E
Eric Blake 已提交
225
    bool (*handler) (vshControl *, const vshCmd *);    /* command handler */
226 227
    const vshCmdOptDef *opts;   /* definition of command options */
    const vshCmdInfo *info;     /* details about command */
228
    unsigned int flags;         /* bitwise OR of VSH_CMD_FLAG */
K
Karel Zak 已提交
229 230 231 232 233 234
} vshCmdDef;

/*
 * vshCmd - parsed command
 */
typedef struct __vshCmd {
235
    const vshCmdDef *def;       /* command definition */
236 237
    vshCmdOpt *opts;            /* list of command arguments */
    struct __vshCmd *next;      /* next command */
K
Karel Zak 已提交
238 239 240 241 242 243
} __vshCmd;

/*
 * vshControl
 */
typedef struct __vshControl {
K
Karel Zak 已提交
244
    char *name;                 /* connection name */
245
    virConnectPtr conn;         /* connection to hypervisor (MAY BE NULL) */
246 247
    vshCmd *cmd;                /* the current command */
    char *cmdstr;               /* string with command */
E
Eric Blake 已提交
248 249
    bool imode;                 /* interactive mode? */
    bool quiet;                 /* quiet mode */
250
    int debug;                  /* print debug messages? */
E
Eric Blake 已提交
251 252
    bool timing;                /* print timing info? */
    bool readonly;              /* connect readonly (first time only, not
253 254
                                 * during explicit connect command)
                                 */
255 256
    char *logfile;              /* log file name */
    int log_fd;                 /* log file descriptor */
257 258
    char *historydir;           /* readline history directory name */
    char *historyfile;          /* readline history file name */
259 260
    bool useGetInfo;            /* must use virDomainGetInfo, since
                                   virDomainGetState is not supported */
261 262
    bool useSnapshotOld;        /* cannot use virDomainSnapshotGetParent or
                                   virDomainSnapshotNumChildren */
J
Jiri Denemark 已提交
263
    virThread eventLoop;
264
    virMutex lock;
J
Jiri Denemark 已提交
265 266
    bool eventLoopStarted;
    bool quit;
267 268 269

    const char *escapeChar;     /* String representation of
                                   console escape character */
K
Karel Zak 已提交
270
} __vshControl;
271

272
typedef struct vshCmdGrp {
E
Eric Blake 已提交
273
    const char *name;    /* name of group, or NULL for list end */
274 275 276
    const char *keyword; /* help keyword */
    const vshCmdDef *commands;
} vshCmdGrp;
277

278
static const vshCmdGrp cmdGroups[];
K
Karel Zak 已提交
279

280 281
static void vshError(vshControl *ctl, const char *format, ...)
    ATTRIBUTE_FMT_PRINTF(2, 3);
E
Eric Blake 已提交
282 283
static bool vshInit(vshControl *ctl);
static bool vshDeinit(vshControl *ctl);
284
static void vshUsage(void);
285
static void vshOpenLogFile(vshControl *ctl);
286 287
static void vshOutputLogFile(vshControl *ctl, int log_level, const char *format, va_list ap)
    ATTRIBUTE_FMT_PRINTF(3, 0);
288
static void vshCloseLogFile(vshControl *ctl);
K
Karel Zak 已提交
289

E
Eric Blake 已提交
290
static bool vshParseArgv(vshControl *ctl, int argc, char **argv);
K
Karel Zak 已提交
291

292
static const char *vshCmddefGetInfo(const vshCmdDef *cmd, const char *info);
293
static const vshCmdDef *vshCmddefSearch(const char *cmdname);
E
Eric Blake 已提交
294
static bool vshCmddefHelp(vshControl *ctl, const char *name);
295
static const vshCmdGrp *vshCmdGrpSearch(const char *grpname);
E
Eric Blake 已提交
296
static bool vshCmdGrpHelp(vshControl *ctl, const char *name);
K
Karel Zak 已提交
297

E
Eric Blake 已提交
298 299 300
static int vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
    ATTRIBUTE_RETURN_CHECK;
301 302
static int vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
303 304 305
static int vshCommandOptUInt(const vshCmd *cmd, const char *name,
                             unsigned int *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
306 307 308 309 310 311 312 313 314
static int vshCommandOptUL(const vshCmd *cmd, const char *name,
                           unsigned long *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
static int vshCommandOptString(const vshCmd *cmd, const char *name,
                               const char **value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
static int vshCommandOptLongLong(const vshCmd *cmd, const char *name,
                                 long long *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
315 316 317
static int vshCommandOptULongLong(const vshCmd *cmd, const char *name,
                                  unsigned long long *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
E
Eric Blake 已提交
318 319 320 321
static int vshCommandOptScaledInt(const vshCmd *cmd, const char *name,
                                  unsigned long long *value, int scale,
                                  unsigned long long max)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
E
Eric Blake 已提交
322
static bool vshCommandOptBool(const vshCmd *cmd, const char *name);
323 324
static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd,
                                          const vshCmdOpt *opt);
325 326 327
static char *vshGetDomainDescription(vshControl *ctl, virDomainPtr dom,
                                     bool title, unsigned int flags)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
K
Karel Zak 已提交
328

329 330 331
#define VSH_BYID     (1 << 1)
#define VSH_BYUUID   (1 << 2)
#define VSH_BYNAME   (1 << 3)
332
#define VSH_BYMAC    (1 << 4)
K
Karel Zak 已提交
333

334
static virDomainPtr vshCommandOptDomainBy(vshControl *ctl, const vshCmd *cmd,
335
                                          const char **name, int flag);
K
Karel Zak 已提交
336 337

/* default is lookup by Id, Name and UUID */
J
Jim Meyering 已提交
338 339
#define vshCommandOptDomain(_ctl, _cmd, _name)                      \
    vshCommandOptDomainBy(_ctl, _cmd, _name, VSH_BYID|VSH_BYUUID|VSH_BYNAME)
340

341
static virSecretPtr vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd,
342
                                        const char **name);
343

344
static void vshPrintExtra(vshControl *ctl, const char *format, ...)
345
    ATTRIBUTE_FMT_PRINTF(2, 3);
346
static void vshDebug(vshControl *ctl, int level, const char *format, ...)
347
    ATTRIBUTE_FMT_PRINTF(3, 4);
K
Karel Zak 已提交
348 349

/* XXX: add batch support */
350
#define vshPrint(_ctl, ...)   vshPrintExtra(NULL, __VA_ARGS__)
K
Karel Zak 已提交
351

352
static int vshDomainState(vshControl *ctl, virDomainPtr dom, int *reason);
K
Karel Zak 已提交
353
static const char *vshDomainStateToString(int state);
354
static const char *vshDomainStateReasonToString(int state, int reason);
355
static const char *vshDomainControlStateToString(int state);
356
static const char *vshDomainVcpuStateToString(int state);
E
Eric Blake 已提交
357
static bool vshConnectionUsability(vshControl *ctl, virConnectPtr conn);
358 359 360
static virTypedParameterPtr vshFindTypedParamByName(const char *name,
                                                    virTypedParameterPtr list,
                                                    int count);
361 362
static char *vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
K
Karel Zak 已提交
363

E
Eric Blake 已提交
364 365 366
static char *editWriteToTempFile(vshControl *ctl, const char *doc);
static int   editFile(vshControl *ctl, const char *filename);
static char *editReadBackFile(vshControl *ctl, const char *filename);
367

368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
/* Typedefs, function prototypes for job progress reporting.
 * There are used by some long lingering commands like
 * migrate, dump, save, managedsave.
 */
typedef struct __vshCtrlData {
    vshControl *ctl;
    const vshCmd *cmd;
    int writefd;
} vshCtrlData;

typedef void (*jobWatchTimeoutFunc) (vshControl *ctl, virDomainPtr dom,
                                     void *opaque);

static bool
vshWatchJob(vshControl *ctl,
            virDomainPtr dom,
            bool verbose,
            int pipe_fd,
            int timeout,
            jobWatchTimeoutFunc timeout_func,
            void *opaque,
            const char *label);

391
static void *_vshMalloc(vshControl *ctl, size_t sz, const char *filename, int line);
392 393
#define vshMalloc(_ctl, _sz)    _vshMalloc(_ctl, _sz, __FILE__, __LINE__)

394
static void *_vshCalloc(vshControl *ctl, size_t nmemb, size_t sz, const char *filename, int line);
395 396
#define vshCalloc(_ctl, _nmemb, _sz)    _vshCalloc(_ctl, _nmemb, _sz, __FILE__, __LINE__)

397
static char *_vshStrdup(vshControl *ctl, const char *s, const char *filename, int line);
398 399
#define vshStrdup(_ctl, _s)    _vshStrdup(_ctl, _s, __FILE__, __LINE__)

400 401
static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate);

E
Eric Blake 已提交
402 403 404
static void *
_vshMalloc(vshControl *ctl, size_t size, const char *filename, int line)
{
E
Eric Blake 已提交
405
    char *x;
E
Eric Blake 已提交
406

E
Eric Blake 已提交
407
    if (VIR_ALLOC_N(x, size) == 0)
E
Eric Blake 已提交
408 409 410 411 412 413 414 415 416
        return x;
    vshError(ctl, _("%s: %d: failed to allocate %d bytes"),
             filename, line, (int) size);
    exit(EXIT_FAILURE);
}

static void *
_vshCalloc(vshControl *ctl, size_t nmemb, size_t size, const char *filename, int line)
{
E
Eric Blake 已提交
417
    char *x;
E
Eric Blake 已提交
418

E
Eric Blake 已提交
419 420
    if (!xalloc_oversized(nmemb, size) &&
        VIR_ALLOC_N(x, nmemb * size) == 0)
E
Eric Blake 已提交
421 422 423 424 425 426 427 428 429 430 431 432
        return x;
    vshError(ctl, _("%s: %d: failed to allocate %d bytes"),
             filename, line, (int) (size*nmemb));
    exit(EXIT_FAILURE);
}

static char *
_vshStrdup(vshControl *ctl, const char *s, const char *filename, int line)
{
    char *x;

    if (s == NULL)
433
        return NULL;
E
Eric Blake 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    if ((x = strdup(s)))
        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.  */
#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
450

451 452 453 454 455
static int
vshNameSorter(const void *a, const void *b)
{
    const char **sa = (const char**)a;
    const char **sb = (const char**)b;
456

457 458
    /* User visible sort, so we want locale-specific case comparison. */
    return strcasecmp(*sa, *sb);
459 460
}

461 462 463 464 465 466 467
static double
prettyCapacity(unsigned long long val,
               const char **unit) {
    if (val < 1024) {
        *unit = "";
        return (double)val;
    } else if (val < (1024.0l * 1024.0l)) {
468
        *unit = "KiB";
469 470
        return (((double)val / 1024.0l));
    } else if (val < (1024.0l * 1024.0l * 1024.0l)) {
471
        *unit = "MiB";
472
        return (double)val / (1024.0l * 1024.0l);
473
    } else if (val < (1024.0l * 1024.0l * 1024.0l * 1024.0l)) {
474
        *unit = "GiB";
475
        return (double)val / (1024.0l * 1024.0l * 1024.0l);
476
    } else {
477
        *unit = "TiB";
478
        return (double)val / (1024.0l * 1024.0l * 1024.0l * 1024.0l);
479 480 481 482
    }
}


J
John Levon 已提交
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
static virErrorPtr last_error;

/*
 * Quieten libvirt until we're done with the command.
 */
static void
virshErrorHandler(void *unused ATTRIBUTE_UNUSED, virErrorPtr error)
{
    virFreeError(last_error);
    last_error = virSaveLastError();
    if (getenv("VIRSH_DEBUG") != NULL)
        virDefaultErrorFunc(error);
}

/*
 * Report an error when a command finishes.  This is better than before
 * (when correct operation would report errors), but it has some
 * problems: we lose the smarter formatting of virDefaultErrorFunc(),
 * and it can become harder to debug problems, if errors get reported
 * twice during one command.  This case shouldn't really happen anyway,
 * and it's IMHO a bug that libvirt does that sometimes.
 */
static void
virshReportError(vshControl *ctl)
{
508 509 510 511 512 513 514 515
    if (last_error == NULL) {
        /* Calling directly into libvirt util functions won't trigger the
         * error callback (which sets last_error), so check it ourselves.
         *
         * If the returned error has CODE_OK, this most likely means that
         * no error was ever raised, so just ignore */
        last_error = virSaveLastError();
        if (!last_error || last_error->code == VIR_ERR_OK)
516
            goto out;
517
    }
J
John Levon 已提交
518 519

    if (last_error->code == VIR_ERR_OK) {
520
        vshError(ctl, "%s", _("unknown error"));
J
John Levon 已提交
521 522 523
        goto out;
    }

524
    vshError(ctl, "%s", last_error->message);
J
John Levon 已提交
525 526 527 528 529 530

out:
    virFreeError(last_error);
    last_error = NULL;
}

531 532 533 534 535 536 537 538 539
static volatile sig_atomic_t intCaught = 0;

static void vshCatchInt(int sig ATTRIBUTE_UNUSED,
                        siginfo_t *siginfo ATTRIBUTE_UNUSED,
                        void *context ATTRIBUTE_UNUSED)
{
    intCaught = 1;
}

540 541 542 543 544
/*
 * Detection of disconnections and automatic reconnection support
 */
static int disconnected = 0; /* we may have been disconnected */

545 546 547 548 549
/* Gnulib doesn't guarantee SA_SIGINFO support.  */
#ifndef SA_SIGINFO
# define SA_SIGINFO 0
#endif

550 551 552 553 554 555
/*
 * vshCatchDisconnect:
 *
 * We get here when a SIGPIPE is being raised, we can't do much in the
 * handler, just save the fact it was raised
 */
556 557
static void vshCatchDisconnect(int sig, siginfo_t *siginfo,
                               void *context ATTRIBUTE_UNUSED) {
E
Eric Blake 已提交
558
    if (sig == SIGPIPE ||
559
        (SA_SIGINFO && siginfo->si_signo == SIGPIPE))
560 561 562 563 564 565 566 567 568
        disconnected++;
}

/*
 * vshSetupSignals:
 *
 * Catch SIGPIPE signals which may arise when disconnection
 * from libvirtd occurs
 */
L
Laine Stump 已提交
569
static void
570 571 572 573 574 575 576 577 578 579 580 581 582
vshSetupSignals(void) {
    struct sigaction sig_action;

    sig_action.sa_sigaction = vshCatchDisconnect;
    sig_action.sa_flags = SA_SIGINFO;
    sigemptyset(&sig_action.sa_mask);

    sigaction(SIGPIPE, &sig_action, NULL);
}

/*
 * vshReconnect:
 *
L
Laine Stump 已提交
583
 * Reconnect after a disconnect from libvirtd
584 585
 *
 */
L
Laine Stump 已提交
586
static void
587 588 589 590 591 592
vshReconnect(vshControl *ctl)
{
    bool connected = false;

    if (ctl->conn != NULL) {
        connected = true;
593
        virConnectClose(ctl->conn);
594
    }
595 596 597 598 599 600

    ctl->conn = virConnectOpenAuth(ctl->name,
                                   virConnectAuthPtrDefault,
                                   ctl->readonly ? VIR_CONNECT_RO : 0);
    if (!ctl->conn)
        vshError(ctl, "%s", _("Failed to reconnect to the hypervisor"));
601
    else if (connected)
602 603
        vshError(ctl, "%s", _("Reconnected to the hypervisor"));
    disconnected = 0;
604
    ctl->useGetInfo = false;
605
    ctl->useSnapshotOld = false;
606
}
607

608
#ifndef WIN32
609 610 611 612 613 614 615 616 617 618 619 620 621
static void
vshPrintRaw(vshControl *ctl, ...)
{
    va_list ap;
    char *key;

    va_start(ap, ctl);
    while ((key = va_arg(ap, char *)) != NULL) {
        vshPrint(ctl, "%s\r\n", key);
    }
    va_end(ap);
}

622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
/**
 * vshAskReedit:
 * @msg: Question to ask user
 *
 * Ask user if he wants to return to previously
 * edited file.
 *
 * Returns 'y' if he wants to
 *         'f' if he forcibly wants to
 *         'n' if he doesn't want to
 *         -1  on error
 *          0  otherwise
 */
static int
vshAskReedit(vshControl *ctl, const char *msg)
{
    int c = -1;
    struct termios ttyattr;

    if (!isatty(STDIN_FILENO))
        return -1;

    virshReportError(ctl);

    if (vshMakeStdinRaw(&ttyattr, false) < 0)
        return -1;

    while (true) {
        /* TRANSLATORS: For now, we aren't using LC_MESSAGES, and the user
         * choices really are limited to just 'y', 'n', 'f' and '?'  */
        vshPrint(ctl, "\r%s %s", msg, _("Try again? [y,n,f,?]:"));
        c = c_tolower(getchar());

        if (c == '?') {
656 657 658 659 660 661 662
            vshPrintRaw(ctl,
                        "",
                        _("y - yes, start editor again"),
                        _("n - no, throw away my changes"),
                        _("f - force, try to redefine again"),
                        _("? - print this help"),
                        NULL);
663 664 665 666 667 668 669 670 671 672
            continue;
        } else if (c == 'y' || c == 'n' || c == 'f') {
            break;
        }
    }

    tcsetattr(STDIN_FILENO, TCSAFLUSH, &ttyattr);

    vshPrint(ctl, "\r\n");
    return c;
673 674 675 676 677
}
#else /* WIN32 */
static int
vshAskReedit(vshControl *ctl, const char *msg ATTRIBUTE_UNUSED)
{
678 679 680 681
    vshDebug(ctl, VSH_ERR_WARNING, "%s", _("This function is not "
                                           "supported on WIN32 platform"));
    return 0;
}
682
#endif /* WIN32 */
683

684 685 686 687 688 689 690 691
static int vshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
                         const char *bytes, size_t nbytes, void *opaque)
{
    int *fd = opaque;

    return safewrite(*fd, bytes, nbytes);
}

K
Karel Zak 已提交
692 693 694 695 696 697
/* ---------------
 * Commands
 * ---------------
 */

/*
698
 * "help" command
K
Karel Zak 已提交
699
 */
700
static const vshCmdInfo info_help[] = {
701
    {"help", N_("print help")},
702 703
    {"desc", N_("Prints global help, command specific help, or help for a\n"
                "    group of related commands")},
704

705
    {NULL, NULL}
K
Karel Zak 已提交
706 707
};

708
static const vshCmdOptDef opts_help[] = {
709
    {"command", VSH_OT_DATA, 0, N_("Prints global help, command specific help, or help for a group of related commands")},
710
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
711 712
};

E
Eric Blake 已提交
713
static bool
714
cmdHelp(vshControl *ctl, const vshCmd *cmd)
715
 {
716
    const char *name = NULL;
717

718
    if (vshCommandOptString(cmd, "command", &name) <= 0) {
719
        const vshCmdGrp *grp;
720
        const vshCmdDef *def;
721

722 723 724 725 726 727
        vshPrint(ctl, "%s", _("Grouped commands:\n\n"));

        for (grp = cmdGroups; grp->name; grp++) {
            vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name,
                     grp->keyword);

728 729 730
            for (def = grp->commands; def->name; def++) {
                if (def->flags & VSH_CMD_FLAG_ALIAS)
                    continue;
731 732
                vshPrint(ctl, "    %-30s %s\n", def->name,
                         _(vshCmddefGetInfo(def, "help")));
733
            }
734 735 736 737

            vshPrint(ctl, "\n");
        }

E
Eric Blake 已提交
738
        return true;
739
    }
740

E
Eric Blake 已提交
741
    if (vshCmddefSearch(name)) {
742
        return vshCmddefHelp(ctl, name);
E
Eric Blake 已提交
743
    } else if (vshCmdGrpSearch(name)) {
744 745 746
        return vshCmdGrpHelp(ctl, name);
    } else {
        vshError(ctl, _("command or command group '%s' doesn't exist"), name);
E
Eric Blake 已提交
747
        return false;
K
Karel Zak 已提交
748 749 750 751
    }
}

/*
752
 * "connect" command
K
Karel Zak 已提交
753
 */
754
static const vshCmdInfo info_connect[] = {
755
    {"help", N_("(re)connect to hypervisor")},
756
    {"desc",
757
     N_("Connect to local hypervisor. This is built-in command after shell start up.")},
758
    {NULL, NULL}
K
Karel Zak 已提交
759 760
};

761
static const vshCmdOptDef opts_connect[] = {
E
Eric Blake 已提交
762 763
    {"name",     VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
     N_("hypervisor connection URI")},
764
    {"readonly", VSH_OT_BOOL, 0, N_("read-only connection")},
765
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
766 767
};

E
Eric Blake 已提交
768
static bool
769
cmdConnect(vshControl *ctl, const vshCmd *cmd)
770
{
E
Eric Blake 已提交
771
    bool ro = vshCommandOptBool(cmd, "readonly");
772
    const char *name = NULL;
773

K
Karel Zak 已提交
774
    if (ctl->conn) {
775 776 777
        int ret;
        if ((ret = virConnectClose(ctl->conn)) != 0) {
            vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
E
Eric Blake 已提交
778
            return false;
K
Karel Zak 已提交
779 780 781
        }
        ctl->conn = NULL;
    }
782

783
    VIR_FREE(ctl->name);
784 785
    if (vshCommandOptString(cmd, "name", &name) < 0) {
        vshError(ctl, "%s", _("Please specify valid connection URI"));
E
Eric Blake 已提交
786
        return false;
787
    }
788
    ctl->name = vshStrdup(ctl, name);
K
Karel Zak 已提交
789

790
    ctl->useGetInfo = false;
791
    ctl->useSnapshotOld = false;
E
Eric Blake 已提交
792
    ctl->readonly = ro;
K
Karel Zak 已提交
793

794 795 796
    ctl->conn = virConnectOpenAuth(ctl->name, virConnectAuthPtrDefault,
                                   ctl->readonly ? VIR_CONNECT_RO : 0);

K
Karel Zak 已提交
797
    if (!ctl->conn)
798
        vshError(ctl, "%s", _("Failed to connect to the hypervisor"));
799

E
Eric Blake 已提交
800
    return !!ctl->conn;
K
Karel Zak 已提交
801 802
}

803
/*
804
 * "freecell" command
805
 */
806 807 808
static const vshCmdInfo info_freecell[] = {
    {"help", N_("NUMA free memory")},
    {"desc", N_("display available free memory for the NUMA cell.")},
809 810 811
    {NULL, NULL}
};

812 813 814
static const vshCmdOptDef opts_freecell[] = {
    {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
    {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
815 816 817
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
818
static bool
819
cmdFreecell(vshControl *ctl, const vshCmd *cmd)
820
{
821 822 823 824 825 826 827 828 829 830 831 832 833
    bool func_ret = false;
    int ret;
    int cell = -1, cell_given;
    unsigned long long memory;
    xmlNodePtr *nodes = NULL;
    unsigned long nodes_cnt;
    unsigned long *nodes_id = NULL;
    unsigned long long *nodes_free = NULL;
    int all_given;
    int i;
    char *cap_xml = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
834

835

836 837 838 839 840
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
        vshError(ctl, "%s", _("cell number has to be a number"));
841 842
        goto cleanup;
    }
843
    all_given = vshCommandOptBool(cmd, "all");
844

845 846 847
    if (all_given && cell_given) {
        vshError(ctl, "%s", _("--cellno and --all are mutually exclusive. "
                              "Please choose only one."));
848 849 850
        goto cleanup;
    }

851 852 853 854 855 856
    if (all_given) {
        cap_xml = virConnectGetCapabilities(ctl->conn);
        if (!cap_xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
            goto cleanup;
        }
857

858 859 860 861 862 863 864
        xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
        if (!xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
            goto cleanup;
        }
        nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
                                    ctxt, &nodes);
865

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
        if (nodes_cnt == -1) {
            vshError(ctl, "%s", _("could not get information about "
                                  "NUMA topology"));
            goto cleanup;
        }

        nodes_free = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_free));
        nodes_id = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_id));

        for (i = 0; i < nodes_cnt; i++) {
            unsigned long id;
            char *val = virXMLPropString(nodes[i], "id");
            if (virStrToLong_ul(val, NULL, 10, &id)) {
                vshError(ctl, "%s", _("conversion from string failed"));
                VIR_FREE(val);
                goto cleanup;
            }
            VIR_FREE(val);
            nodes_id[i]=id;
            ret = virNodeGetCellsFreeMemory(ctl->conn, &(nodes_free[i]), id, 1);
            if (ret != 1) {
                vshError(ctl, _("failed to get free memory for NUMA node "
                                "number: %lu"), id);
                goto cleanup;
            }
        }

        memory = 0;
        for (cell = 0; cell < nodes_cnt; cell++) {
            vshPrint(ctl, "%5lu: %10llu KiB\n", nodes_id[cell],
                    (nodes_free[cell]/1024));
            memory += nodes_free[cell];
        }

        vshPrintExtra(ctl, "--------------------\n");
        vshPrintExtra(ctl, "%5s: %10llu KiB\n", _("Total"), memory/1024);
    } else {
        if (!cell_given) {
            memory = virNodeGetFreeMemory(ctl->conn);
            if (memory == 0)
                goto cleanup;
        } else {
            ret = virNodeGetCellsFreeMemory(ctl->conn, &memory, cell, 1);
            if (ret != 1)
                goto cleanup;
        }

        if (cell == -1)
            vshPrint(ctl, "%s: %llu KiB\n", _("Total"), (memory/1024));
        else
            vshPrint(ctl, "%d: %llu KiB\n", cell, (memory/1024));
    }

    func_ret = true;

cleanup:
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
    VIR_FREE(nodes);
    VIR_FREE(nodes_free);
    VIR_FREE(nodes_id);
    VIR_FREE(cap_xml);
    return func_ret;
929 930
}

931 932 933 934 935 936 937 938 939
/*
 * "nodeinfo" command
 */
static const vshCmdInfo info_nodeinfo[] = {
    {"help", N_("node information")},
    {"desc", N_("Returns basic information about the node.")},
    {NULL, NULL}
};

E
Eric Blake 已提交
940
static bool
941
cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
942
{
943
    virNodeInfo info;
944

945
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
946
        return false;
947

948 949
    if (virNodeGetInfo(ctl->conn, &info) < 0) {
        vshError(ctl, "%s", _("failed to get node information"));
E
Eric Blake 已提交
950
        return false;
951
    }
952 953 954 955 956 957 958 959
    vshPrint(ctl, "%-20s %s\n", _("CPU model:"), info.model);
    vshPrint(ctl, "%-20s %d\n", _("CPU(s):"), info.cpus);
    vshPrint(ctl, "%-20s %d MHz\n", _("CPU frequency:"), info.mhz);
    vshPrint(ctl, "%-20s %d\n", _("CPU socket(s):"), info.sockets);
    vshPrint(ctl, "%-20s %d\n", _("Core(s) per socket:"), info.cores);
    vshPrint(ctl, "%-20s %d\n", _("Thread(s) per core:"), info.threads);
    vshPrint(ctl, "%-20s %d\n", _("NUMA cell(s):"), info.nodes);
    vshPrint(ctl, "%-20s %lu KiB\n", _("Memory size:"), info.memory);
960

961
    return true;
962 963
}

964
/*
965
 * "nodecpustats" command
966
 */
967 968 969
static const vshCmdInfo info_nodecpustats[] = {
    {"help", N_("Prints cpu stats of the node.")},
    {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
970 971 972
    {NULL, NULL}
};

973 974 975
static const vshCmdOptDef opts_node_cpustats[] = {
    {"cpu", VSH_OT_INT, 0, N_("prints specified cpu statistics only.")},
    {"percent", VSH_OT_BOOL, 0, N_("prints by percentage during 1 second.")},
976 977 978 979
    {NULL, 0, 0, NULL}
};

static bool
980
cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
981
{
982 983 984 985 986 987
    int i, j;
    bool flag_utilization = false;
    bool flag_percent = vshCommandOptBool(cmd, "percent");
    int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
    virNodeCPUStatsPtr params;
    int nparams = 0;
988
    bool ret = false;
989 990 991 992 993 994 995 996 997
    struct cpu_stats {
        unsigned long long user;
        unsigned long long sys;
        unsigned long long idle;
        unsigned long long iowait;
        unsigned long long util;
    } cpu_stats[2];
    double user_time, sys_time, idle_time, iowait_time, total_time;
    double usage;
998

999 1000 1001
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

1002 1003
    if (vshCommandOptInt(cmd, "cpu", &cpuNum) < 0) {
        vshError(ctl, "%s", _("Invalid value of cpuNum"));
1004
        return false;
1005
    }
1006

1007 1008 1009 1010
    if (virNodeGetCPUStats(ctl->conn, cpuNum, NULL, &nparams, 0) != 0) {
        vshError(ctl, "%s",
                 _("Unable to get number of cpu stats"));
        return false;
1011
    }
1012 1013 1014
    if (nparams == 0) {
        /* nothing to output */
        return true;
1015
    }
1016

1017 1018
    memset(cpu_stats, 0, sizeof(cpu_stats));
    params = vshCalloc(ctl, nparams, sizeof(*params));
1019

1020 1021 1022
    for (i = 0; i < 2; i++) {
        if (i > 0)
            sleep(1);
1023

1024 1025 1026
        if (virNodeGetCPUStats(ctl->conn, cpuNum, params, &nparams, 0) != 0) {
            vshError(ctl, "%s", _("Unable to get node cpu stats"));
            goto cleanup;
1027 1028
        }

1029 1030
        for (j = 0; j < nparams; j++) {
            unsigned long long value = params[j].value;
1031

1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
            if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
                cpu_stats[i].sys = value;
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
                cpu_stats[i].user = value;
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
                cpu_stats[i].idle = value;
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
                cpu_stats[i].iowait = value;
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
                cpu_stats[i].util = value;
                flag_utilization = true;
            }
        }

        if (flag_utilization || !flag_percent)
            break;
1048
    }
1049

1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
    if (!flag_percent) {
        if (!flag_utilization) {
            vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
            vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
            vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
            vshPrint(ctl, "%-15s %20llu\n", _("iowait:"), cpu_stats[0].iowait);
        }
    } else {
        if (flag_utilization) {
            usage = cpu_stats[0].util;
1060

1061 1062 1063 1064 1065 1066 1067 1068
            vshPrint(ctl, "%-15s %5.1lf%%\n", _("usage:"), usage);
            vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
        } else {
            user_time   = cpu_stats[1].user   - cpu_stats[0].user;
            sys_time    = cpu_stats[1].sys    - cpu_stats[0].sys;
            idle_time   = cpu_stats[1].idle   - cpu_stats[0].idle;
            iowait_time = cpu_stats[1].iowait - cpu_stats[0].iowait;
            total_time  = user_time + sys_time + idle_time + iowait_time;
1069

1070
            usage = (user_time + sys_time) / total_time * 100;
1071

1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("usage:"), usage);
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("user:"), user_time / total_time * 100);
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("system:"), sys_time  / total_time * 100);
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("idle:"), idle_time     / total_time * 100);
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("iowait:"), iowait_time   / total_time * 100);
1082 1083
        }
    }
1084

1085
    ret = true;
1086 1087 1088

  cleanup:
    VIR_FREE(params);
1089 1090 1091
    return ret;
}

1092
/*
1093
 * "nodememstats" command
1094
 */
1095 1096 1097
static const vshCmdInfo info_nodememstats[] = {
    {"help", N_("Prints memory stats of the node.")},
    {"desc", N_("Returns memory stats of the node, in kilobytes.")},
1098 1099 1100
    {NULL, NULL}
};

1101 1102
static const vshCmdOptDef opts_node_memstats[] = {
    {"cell", VSH_OT_INT, 0, N_("prints specified cell statistics only.")},
1103 1104 1105
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1106
static bool
1107
cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd)
1108
{
1109 1110 1111 1112
    int nparams = 0;
    unsigned int i = 0;
    int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
    virNodeMemoryStatsPtr params = NULL;
1113
    bool ret = false;
1114

1115
    if (!vshConnectionUsability(ctl, ctl->conn))
1116
        return false;
1117

1118 1119 1120
    if (vshCommandOptInt(cmd, "cell", &cellNum) < 0) {
        vshError(ctl, "%s", _("Invalid value of cellNum"));
        return false;
1121
    }
1122

1123 1124 1125 1126
    /* get the number of memory parameters */
    if (virNodeGetMemoryStats(ctl->conn, cellNum, NULL, &nparams, 0) != 0) {
        vshError(ctl, "%s",
                 _("Unable to get number of memory stats"));
1127 1128 1129
        goto cleanup;
    }

1130 1131 1132
    if (nparams == 0) {
        /* nothing to output */
        ret = true;
1133 1134
        goto cleanup;
    }
1135

1136 1137 1138 1139
    /* now go get all the memory parameters */
    params = vshCalloc(ctl, nparams, sizeof(*params));
    if (virNodeGetMemoryStats(ctl->conn, cellNum, params, &nparams, 0) != 0) {
        vshError(ctl, "%s", _("Unable to get memory stats"));
1140 1141
        goto cleanup;
    }
1142

1143 1144
    for (i = 0; i < nparams; i++)
        vshPrint(ctl, "%-7s: %20llu KiB\n", params[i].field, params[i].value);
1145

1146
    ret = true;
1147

1148 1149 1150 1151
  cleanup:
    VIR_FREE(params);
    return ret;
}
1152

1153 1154 1155 1156 1157 1158 1159 1160 1161
/*
 * "nodesuspend" command
 */
static const vshCmdInfo info_nodesuspend[] = {
    {"help", N_("suspend the host node for a given time duration")},
    {"desc", N_("Suspend the host node for a given time duration "
                               "and attempt to resume thereafter.")},
    {NULL, NULL}
};
1162

1163 1164 1165 1166 1167 1168 1169
static const vshCmdOptDef opts_node_suspend[] = {
    {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
                                               "disk(Suspend-to-Disk), hybrid(Hybrid-Suspend)")},
    {"duration", VSH_OT_INT, VSH_OFLAG_REQ, N_("Suspend duration in seconds")},
    {"flags", VSH_OT_INT, VSH_OFLAG_NONE, N_("Suspend flags, 0 for default")},
    {NULL, 0, 0, NULL}
};
1170

1171 1172 1173 1174 1175 1176 1177
static bool
cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
{
    const char *target = NULL;
    unsigned int suspendTarget;
    long long duration;
    unsigned int flags = 0;
1178

1179 1180
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
1181

1182 1183 1184
    if (vshCommandOptString(cmd, "target", &target) < 0) {
        vshError(ctl, _("Invalid target argument"));
        return false;
1185 1186
    }

1187 1188 1189
    if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
        vshError(ctl, _("Invalid duration argument"));
        return false;
1190
    }
1191

1192 1193 1194
    if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
        vshError(ctl, _("Invalid flags argument"));
        return false;
1195
    }
1196

1197 1198 1199 1200 1201 1202 1203 1204 1205
    if (STREQ(target, "mem"))
        suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
    else if (STREQ(target, "disk"))
        suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
    else if (STREQ(target, "hybrid"))
        suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
    else {
        vshError(ctl, "%s", _("Invalid target"));
        return false;
1206
    }
1207 1208 1209 1210

    if (duration <= 0) {
        vshError(ctl, "%s", _("Invalid duration"));
        return false;
1211 1212
    }

1213 1214 1215 1216
    if (virNodeSuspendForDuration(ctl->conn, suspendTarget, duration,
                                  flags) < 0) {
        vshError(ctl, "%s", _("The host was not suspended"));
        return false;
1217
    }
1218 1219
    return true;
}
1220

1221

1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
/*
 * "capabilities" command
 */
static const vshCmdInfo info_capabilities[] = {
    {"help", N_("capabilities")},
    {"desc", N_("Returns capabilities of hypervisor/driver.")},
    {NULL, NULL}
};

static bool
cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *caps;

    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    if ((caps = virConnectGetCapabilities(ctl->conn)) == NULL) {
        vshError(ctl, "%s", _("failed to get capabilities"));
        return false;
1242
    }
1243 1244
    vshPrint(ctl, "%s\n", caps);
    VIR_FREE(caps);
1245

1246
    return true;
1247 1248
}

1249
/*
1250
 * "nodedev-create" command
1251
 */
1252 1253 1254 1255 1256 1257
static const vshCmdInfo info_node_device_create[] = {
    {"help", N_("create a device defined "
                          "by an XML file on the node")},
    {"desc", N_("Create a device on the node.  Note that this "
                          "command creates devices on the physical host "
                          "that can then be assigned to a virtual machine.")},
1258 1259
    {NULL, NULL}
};
1260

1261 1262 1263
static const vshCmdOptDef opts_node_device_create[] = {
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
     N_("file containing an XML description of the device")},
1264 1265
    {NULL, 0, 0, NULL}
};
1266

1267
static bool
1268
cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
1269
{
1270 1271
    virNodeDevicePtr dev = NULL;
    const char *from = NULL;
1272
    bool ret = true;
1273
    char *buffer;
1274

1275
    if (!vshConnectionUsability(ctl, ctl->conn))
1276 1277
        return false;

1278 1279 1280 1281
    if (vshCommandOptString(cmd, "file", &from) <= 0)
        return false;

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
1282 1283
        return false;

1284 1285 1286 1287 1288 1289 1290
    dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
    VIR_FREE(buffer);

    if (dev != NULL) {
        vshPrint(ctl, _("Node device %s created from %s\n"),
                 virNodeDeviceGetName(dev), from);
        virNodeDeviceFree(dev);
1291
    } else {
1292
        vshError(ctl, _("Failed to create node device from %s"), from);
1293 1294
        ret = false;
    }
1295

1296 1297
    return ret;
}
1298

1299 1300

/*
1301
 * "nodedev-destroy" command
1302
 */
1303 1304 1305 1306
static const vshCmdInfo info_node_device_destroy[] = {
    {"help", N_("destroy (stop) a device on the node")},
    {"desc", N_("Destroy a device on the node.  Note that this "
                "command destroys devices on the physical host")},
1307 1308 1309
    {NULL, NULL}
};

1310 1311 1312
static const vshCmdOptDef opts_node_device_destroy[] = {
    {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
     N_("name of the device to be destroyed")},
1313 1314 1315
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1316
static bool
1317
cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
1318
{
1319 1320 1321
    virNodeDevicePtr dev = NULL;
    bool ret = true;
    const char *name = NULL;
1322

1323
    if (!vshConnectionUsability(ctl, ctl->conn)) {
E
Eric Blake 已提交
1324
        return false;
1325
    }
1326

1327
    if (vshCommandOptString(cmd, "name", &name) <= 0)
E
Eric Blake 已提交
1328
        return false;
1329

1330
    dev = virNodeDeviceLookupByName(ctl->conn, name);
1331

1332 1333 1334 1335 1336 1337 1338 1339 1340
    if (virNodeDeviceDestroy(dev) == 0) {
        vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
    } else {
        vshError(ctl, _("Failed to destroy node device '%s'"), name);
        ret = false;
    }

    virNodeDeviceFree(dev);
    return ret;
1341 1342
}

1343
/*
1344
 * "secret-define" command
1345
 */
1346 1347 1348
static const vshCmdInfo info_secret_define[] = {
    {"help", N_("define or modify a secret from an XML file")},
    {"desc", N_("Define or modify a secret.")},
1349 1350 1351
    {NULL, NULL}
};

1352 1353
static const vshCmdOptDef opts_secret_define[] = {
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
1354 1355 1356
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1357
static bool
1358
cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
1359
{
1360
    const char *from = NULL;
1361
    char *buffer;
1362 1363
    virSecretPtr res;
    char uuid[VIR_UUID_STRING_BUFLEN];
1364

1365
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1366
        return false;
1367

1368
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
1369
        return false;
1370

1371
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
1372
        return false;
1373

1374
    res = virSecretDefineXML(ctl->conn, buffer, 0);
1375 1376
    VIR_FREE(buffer);

1377 1378 1379 1380 1381 1382 1383
    if (res == NULL) {
        vshError(ctl, _("Failed to set attributes from %s"), from);
        return false;
    }
    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
        virSecretFree(res);
E
Eric Blake 已提交
1384
        return false;
1385
    }
1386 1387
    vshPrint(ctl, _("Secret %s created\n"), uuid);
    virSecretFree(res);
E
Eric Blake 已提交
1388
    return true;
1389 1390
}

1391
/*
1392
 * "secret-dumpxml" command
1393
 */
1394 1395 1396
static const vshCmdInfo info_secret_dumpxml[] = {
    {"help", N_("secret attributes in XML")},
    {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
1397 1398 1399
    {NULL, NULL}
};

1400 1401
static const vshCmdOptDef opts_secret_dumpxml[] = {
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
1402 1403 1404
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1405
static bool
1406
cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
1407
{
1408 1409
    virSecretPtr secret;
    bool ret = false;
1410
    char *xml;
1411

1412
    if (!vshConnectionUsability(ctl, ctl->conn))
1413
        return false;
1414

1415 1416 1417
    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
        return false;
1418

1419 1420
    xml = virSecretGetXMLDesc(secret, 0);
    if (xml == NULL)
W
Wen Congyang 已提交
1421
        goto cleanup;
1422
    vshPrint(ctl, "%s", xml);
1423
    VIR_FREE(xml);
1424
    ret = true;
1425

1426 1427 1428
cleanup:
    virSecretFree(secret);
    return ret;
1429 1430 1431
}

/*
1432
 * "secret-set-value" command
1433
 */
1434 1435 1436
static const vshCmdInfo info_secret_set_value[] = {
    {"help", N_("set a secret value")},
    {"desc", N_("Set a secret value.")},
1437 1438 1439
    {NULL, NULL}
};

1440 1441 1442
static const vshCmdOptDef opts_secret_set_value[] = {
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
    {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
1443 1444 1445
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1446
static bool
1447
cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
1448
{
1449 1450 1451 1452 1453 1454
    virSecretPtr secret;
    size_t value_size;
    const char *base64 = NULL;
    char *value;
    int res;
    bool ret = false;
1455

1456
    if (!vshConnectionUsability(ctl, ctl->conn))
1457
        return false;
1458

1459 1460 1461
    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
        return false;
1462

1463
    if (vshCommandOptString(cmd, "base64", &base64) <= 0)
1464 1465
        goto cleanup;

1466 1467
    if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
        vshError(ctl, "%s", _("Invalid base64 data"));
1468 1469
        goto cleanup;
    }
1470
    if (value == NULL) {
1471
        vshError(ctl, "%s", _("Failed to allocate memory"));
1472
        return false;
1473 1474
    }

1475 1476 1477
    res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
    memset(value, 0, value_size);
    VIR_FREE(value);
J
Jim Fehlig 已提交
1478

1479 1480 1481
    if (res != 0) {
        vshError(ctl, "%s", _("Failed to set secret value"));
        goto cleanup;
1482
    }
1483 1484
    vshPrint(ctl, "%s", _("Secret value set\n"));
    ret = true;
1485

1486 1487 1488
cleanup:
    virSecretFree(secret);
    return ret;
1489 1490 1491
}

/*
1492
 * "secret-get-value" command
1493
 */
1494 1495 1496
static const vshCmdInfo info_secret_get_value[] = {
    {"help", N_("Output a secret value")},
    {"desc", N_("Output a secret value to stdout.")},
1497 1498 1499
    {NULL, NULL}
};

1500 1501
static const vshCmdOptDef opts_secret_get_value[] = {
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
1502 1503 1504
    {NULL, 0, 0, NULL}
};

1505 1506
static bool
cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
1507
{
1508 1509 1510 1511 1512
    virSecretPtr secret;
    char *base64;
    unsigned char *value;
    size_t value_size;
    bool ret = false;
1513

1514 1515
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
1516

1517 1518 1519
    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
        return false;
1520

1521 1522 1523
    value = virSecretGetValue(secret, &value_size, 0);
    if (value == NULL)
        goto cleanup;
1524

1525 1526 1527
    base64_encode_alloc((char *)value, value_size, &base64);
    memset(value, 0, value_size);
    VIR_FREE(value);
1528

1529 1530 1531 1532 1533 1534 1535 1536
    if (base64 == NULL) {
        vshError(ctl, "%s", _("Failed to allocate memory"));
        goto cleanup;
    }
    vshPrint(ctl, "%s", base64);
    memset(base64, 0, strlen(base64));
    VIR_FREE(base64);
    ret = true;
1537

1538 1539 1540
cleanup:
    virSecretFree(secret);
    return ret;
1541 1542
}

1543 1544
/*
 * "secret-undefine" command
1545
 */
1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
static const vshCmdInfo info_secret_undefine[] = {
    {"help", N_("undefine a secret")},
    {"desc", N_("Undefine a secret.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_undefine[] = {
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
    {NULL, 0, 0, NULL}
};
1556

1557 1558
static bool
cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
1559
{
1560 1561 1562
    virSecretPtr secret;
    bool ret = false;
    const char *uuid;
1563

1564 1565
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
1566

1567 1568 1569
    secret = vshCommandOptSecret(ctl, cmd, &uuid);
    if (secret == NULL)
        return false;
1570

1571 1572 1573
    if (virSecretUndefine(secret) < 0) {
        vshError(ctl, _("Failed to delete secret %s"), uuid);
        goto cleanup;
1574
    }
1575 1576
    vshPrint(ctl, _("Secret %s deleted\n"), uuid);
    ret = true;
1577

1578 1579 1580
cleanup:
    virSecretFree(secret);
    return ret;
1581 1582
}

1583 1584 1585 1586 1587 1588 1589 1590 1591
/*
 * "secret-list" command
 */
static const vshCmdInfo info_secret_list[] = {
    {"help", N_("list secrets")},
    {"desc", N_("Returns a list of secrets")},
    {NULL, NULL}
};

E
Eric Blake 已提交
1592
static bool
1593
cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
1594
{
1595 1596
    int maxuuids = 0, i;
    char **uuids = NULL;
1597

1598
    if (!vshConnectionUsability(ctl, ctl->conn))
1599
        return false;
1600

1601 1602 1603 1604
    maxuuids = virConnectNumOfSecrets(ctl->conn);
    if (maxuuids < 0) {
        vshError(ctl, "%s", _("Failed to list secrets"));
        return false;
1605
    }
1606
    uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);
1607

1608 1609 1610 1611 1612
    maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
    if (maxuuids < 0) {
        vshError(ctl, "%s", _("Failed to list secrets"));
        VIR_FREE(uuids);
        return false;
1613 1614
    }

1615
    qsort(uuids, maxuuids, sizeof(char *), vshNameSorter);
1616

1617 1618
    vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
    vshPrintExtra(ctl, "-----------------------------------------------------------\n");
1619

1620 1621 1622
    for (i = 0; i < maxuuids; i++) {
        virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]);
        const char *usageType = NULL;
1623

1624 1625 1626 1627
        if (!sec) {
            VIR_FREE(uuids[i]);
            continue;
        }
1628

1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644
        switch (virSecretGetUsageType(sec)) {
        case VIR_SECRET_USAGE_TYPE_VOLUME:
            usageType = _("Volume");
            break;
        }

        if (usageType) {
            vshPrint(ctl, "%-36s %s %s\n",
                     uuids[i], usageType,
                     virSecretGetUsageID(sec));
        } else {
            vshPrint(ctl, "%-36s %s\n",
                     uuids[i], _("Unused"));
        }
        virSecretFree(sec);
        VIR_FREE(uuids[i]);
1645
    }
1646 1647 1648
    VIR_FREE(uuids);
    return true;
}
1649 1650


1651 1652 1653 1654 1655 1656 1657 1658
/*
 * "version" command
 */
static const vshCmdInfo info_version[] = {
    {"help", N_("show version")},
    {"desc", N_("Display the system version information.")},
    {NULL, NULL}
};
1659

1660 1661 1662 1663
static const vshCmdOptDef opts_version[] = {
    {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
    {NULL, 0, 0, NULL}
};
1664

1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677
static bool
cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    unsigned long hvVersion;
    const char *hvType;
    unsigned long libVersion;
    unsigned long includeVersion;
    unsigned long apiVersion;
    unsigned long daemonVersion;
    int ret;
    unsigned int major;
    unsigned int minor;
    unsigned int rel;
1678

1679 1680 1681 1682 1683 1684 1685
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    hvType = virConnectGetType(ctl->conn);
    if (hvType == NULL) {
        vshError(ctl, "%s", _("failed to get hypervisor type"));
        return false;
1686 1687
    }

1688 1689 1690 1691 1692 1693 1694
    includeVersion = LIBVIR_VERSION_NUMBER;
    major = includeVersion / 1000000;
    includeVersion %= 1000000;
    minor = includeVersion / 1000;
    rel = includeVersion % 1000;
    vshPrint(ctl, _("Compiled against library: libvir %d.%d.%d\n"),
             major, minor, rel);
1695

1696 1697 1698
    ret = virGetVersion(&libVersion, hvType, &apiVersion);
    if (ret < 0) {
        vshError(ctl, "%s", _("failed to get the library version"));
E
Eric Blake 已提交
1699
        return false;
1700
    }
1701 1702 1703 1704 1705 1706
    major = libVersion / 1000000;
    libVersion %= 1000000;
    minor = libVersion / 1000;
    rel = libVersion % 1000;
    vshPrint(ctl, _("Using library: libvir %d.%d.%d\n"),
             major, minor, rel);
1707

1708 1709 1710 1711 1712 1713
    major = apiVersion / 1000000;
    apiVersion %= 1000000;
    minor = apiVersion / 1000;
    rel = apiVersion % 1000;
    vshPrint(ctl, _("Using API: %s %d.%d.%d\n"), hvType,
             major, minor, rel);
1714

1715 1716 1717 1718
    ret = virConnectGetVersion(ctl->conn, &hvVersion);
    if (ret < 0) {
        vshError(ctl, "%s", _("failed to get the hypervisor version"));
        return false;
J
Jim Fehlig 已提交
1719
    }
1720 1721 1722 1723 1724 1725 1726 1727
    if (hvVersion == 0) {
        vshPrint(ctl,
                 _("Cannot extract running %s hypervisor version\n"), hvType);
    } else {
        major = hvVersion / 1000000;
        hvVersion %= 1000000;
        minor = hvVersion / 1000;
        rel = hvVersion % 1000;
1728

1729 1730 1731
        vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"),
                 hvType, major, minor, rel);
    }
1732

1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
    if (vshCommandOptBool(cmd, "daemon")) {
        ret = virConnectGetLibVersion(ctl->conn, &daemonVersion);
        if (ret < 0) {
            vshError(ctl, "%s", _("failed to get the daemon version"));
        } else {
            major = daemonVersion / 1000000;
            daemonVersion %= 1000000;
            minor = daemonVersion / 1000;
            rel = daemonVersion % 1000;
            vshPrint(ctl, _("Running against daemon: %d.%d.%d\n"),
                     major, minor, rel);
        }
J
Jim Fehlig 已提交
1745
    }
1746

1747
    return true;
1748 1749
}

1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764
/* Tree listing helpers.  */

/* 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);

static int
vshTreePrintInternal(vshControl *ctl,
                     vshTreeLookup lookup,
                     void *opaque,
                     int num_devices,
                     int devid,
                     int lastdev,
                     bool root,
                     virBufferPtr indent)
1765
{
1766 1767 1768 1769
    int i;
    int nextlastdev = -1;
    int ret = -1;
    const char *dev = (lookup)(devid, false, opaque);
1770

1771
    if (virBufferError(indent))
1772 1773
        goto cleanup;

1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
    /* Print this device, with indent if not at root */
    vshPrint(ctl, "%s%s%s\n", virBufferCurrentContent(indent),
             root ? "" : "+- ", dev);

    /* Update indent to show '|' or ' ' for child devices */
    if (!root) {
        virBufferAddChar(indent, devid == lastdev ? ' ' : '|');
        virBufferAddChar(indent, ' ');
        if (virBufferError(indent))
            goto cleanup;
1784 1785
    }

1786 1787 1788
    /* Determine the index of the last child device */
    for (i = 0 ; i < num_devices ; i++) {
        const char *parent = (lookup)(i, true, opaque);
1789

1790 1791 1792
        if (parent && STREQ(parent, dev))
            nextlastdev = i;
    }
1793

1794 1795 1796
    /* If there is a child device, then print another blank line */
    if (nextlastdev != -1)
        vshPrint(ctl, "%s  |\n", virBufferCurrentContent(indent));
1797

1798 1799 1800 1801
    /* Finally print all children */
    virBufferAddLit(indent, "  ");
    for (i = 0 ; i < num_devices ; i++) {
        const char *parent = (lookup)(i, true, opaque);
1802

1803 1804 1805 1806 1807
        if (parent && STREQ(parent, dev) &&
            vshTreePrintInternal(ctl, lookup, opaque,
                                 num_devices, i, nextlastdev,
                                 false, indent) < 0)
            goto cleanup;
1808
    }
1809
    virBufferTrim(indent, "  ", -1);
1810

1811 1812 1813 1814
    /* If there was no child device, and we're the last in
     * a list of devices, then print another blank line */
    if (nextlastdev == -1 && devid == lastdev)
        vshPrint(ctl, "%s\n", virBufferCurrentContent(indent));
1815

1816 1817 1818
    if (!root)
        virBufferTrim(indent, NULL, 2);
    ret = 0;
1819 1820 1821 1822
cleanup:
    return ret;
}

1823 1824 1825
static int
vshTreePrint(vshControl *ctl, vshTreeLookup lookup, void *opaque,
             int num_devices, int devid)
1826
{
1827 1828
    int ret;
    virBuffer indent = VIR_BUFFER_INITIALIZER;
1829

1830 1831 1832 1833 1834
    ret = vshTreePrintInternal(ctl, lookup, opaque, num_devices,
                               devid, devid, true, &indent);
    if (ret < 0)
        vshError(ctl, "%s", _("Failed to complete tree listing"));
    virBufferFreeAndReset(&indent);
1835
    return ret;
1836
}
1837

1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849
struct vshNodeList {
    char **names;
    char **parents;
};

static const char *
vshNodeListLookup(int devid, bool parent, void *opaque)
{
    struct vshNodeList *arrays = opaque;
    if (parent)
        return arrays->parents[devid];
    return arrays->names[devid];
1850 1851
}

1852
/*
1853
 * "nodedev-list" command
1854
 */
1855 1856 1857
static const vshCmdInfo info_node_list_devices[] = {
    {"help", N_("enumerate devices on this host")},
    {"desc", ""},
1858 1859 1860
    {NULL, NULL}
};

1861 1862 1863
static const vshCmdOptDef opts_node_list_devices[] = {
    {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
    {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
1864 1865 1866
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1867
static bool
1868
cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
1869
{
1870 1871 1872 1873 1874
    const char *cap = NULL;
    char **devices;
    int num_devices, i;
    bool tree = vshCommandOptBool(cmd, "tree");
    bool ret = true;
1875

1876
    if (!vshConnectionUsability(ctl, ctl->conn))
1877
        return false;
1878

1879 1880
    if (vshCommandOptString(cmd, "cap", &cap) <= 0)
        cap = NULL;
1881

1882 1883 1884 1885 1886 1887 1888
    num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
    if (num_devices < 0) {
        vshError(ctl, "%s", _("Failed to count node devices"));
        return false;
    } else if (num_devices == 0) {
        return true;
    }
1889

1890 1891 1892 1893 1894 1895 1896
    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
    num_devices =
        virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
    if (num_devices < 0) {
        vshError(ctl, "%s", _("Failed to list node devices"));
        VIR_FREE(devices);
        return false;
J
Jim Fehlig 已提交
1897
    }
1898 1899 1900 1901
    qsort(&devices[0], num_devices, sizeof(char*), vshNameSorter);
    if (tree) {
        char **parents = vshMalloc(ctl, sizeof(char *) * num_devices);
        struct vshNodeList arrays = { devices, parents };
J
Jim Fehlig 已提交
1902

1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923
        for (i = 0; i < num_devices; i++) {
            virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]);
            if (dev && STRNEQ(devices[i], "computer")) {
                const char *parent = virNodeDeviceGetParent(dev);
                parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
            } else {
                parents[i] = NULL;
            }
            virNodeDeviceFree(dev);
        }
        for (i = 0 ; i < num_devices ; i++) {
            if (parents[i] == NULL &&
                vshTreePrint(ctl, vshNodeListLookup, &arrays, num_devices,
                             i) < 0)
                ret = false;
        }
        for (i = 0 ; i < num_devices ; i++) {
            VIR_FREE(devices[i]);
            VIR_FREE(parents[i]);
        }
        VIR_FREE(parents);
J
Jim Fehlig 已提交
1924
    } else {
1925 1926 1927 1928
        for (i = 0; i < num_devices; i++) {
            vshPrint(ctl, "%s\n", devices[i]);
            VIR_FREE(devices[i]);
        }
1929
    }
1930 1931
    VIR_FREE(devices);
    return ret;
1932 1933
}

O
Osier Yang 已提交
1934
/*
1935
 * "nodedev-dumpxml" command
O
Osier Yang 已提交
1936
 */
1937 1938 1939
static const vshCmdInfo info_node_device_dumpxml[] = {
    {"help", N_("node device details in XML")},
    {"desc", N_("Output the node device details as an XML dump to stdout.")},
O
Osier Yang 已提交
1940 1941 1942
    {NULL, NULL}
};

1943 1944 1945

static const vshCmdOptDef opts_node_device_dumpxml[] = {
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
O
Osier Yang 已提交
1946 1947 1948 1949
    {NULL, 0, 0, NULL}
};

static bool
1950
cmdNodeDeviceDumpXML(vshControl *ctl, const vshCmd *cmd)
O
Osier Yang 已提交
1951
{
1952 1953 1954
    const char *name = NULL;
    virNodeDevicePtr device;
    char *xml;
O
Osier Yang 已提交
1955

1956 1957 1958 1959 1960 1961 1962
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
    if (vshCommandOptString(cmd, "device", &name) <= 0)
        return false;
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
        return false;
O
Osier Yang 已提交
1963 1964
    }

1965 1966 1967 1968
    xml = virNodeDeviceGetXMLDesc(device, 0);
    if (!xml) {
        virNodeDeviceFree(device);
        return false;
O
Osier Yang 已提交
1969 1970
    }

1971 1972 1973 1974 1975
    vshPrint(ctl, "%s\n", xml);
    VIR_FREE(xml);
    virNodeDeviceFree(device);
    return true;
}
O
Osier Yang 已提交
1976

1977 1978 1979 1980 1981 1982 1983 1984
/*
 * "nodedev-detach" command
 */
static const vshCmdInfo info_node_device_detach[] = {
    {"help", N_("detach node device from its device driver")},
    {"desc", N_("Detach node device from its device driver before assigning to a domain.")},
    {NULL, NULL}
};
O
Osier Yang 已提交
1985 1986


1987 1988 1989 1990
static const vshCmdOptDef opts_node_device_detach[] = {
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
    {NULL, 0, 0, NULL}
};
O
Osier Yang 已提交
1991

1992 1993 1994 1995 1996 1997
static bool
cmdNodeDeviceDetach(vshControl *ctl, const vshCmd *cmd)
{
    const char *name = NULL;
    virNodeDevicePtr device;
    bool ret = true;
O
Osier Yang 已提交
1998

1999 2000 2001 2002 2003 2004 2005
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
    if (vshCommandOptString(cmd, "device", &name) <= 0)
        return false;
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
        return false;
O
Osier Yang 已提交
2006 2007
    }

2008 2009 2010 2011 2012 2013 2014 2015 2016
    /* Yes, our public API is misspelled.  At least virsh can accept
     * either spelling.  */
    if (virNodeDeviceDettach(device) == 0) {
        vshPrint(ctl, _("Device %s detached\n"), name);
    } else {
        vshError(ctl, _("Failed to detach device %s"), name);
        ret = false;
    }
    virNodeDeviceFree(device);
O
Osier Yang 已提交
2017 2018 2019
    return ret;
}

2020
/*
2021
 * "nodedev-reattach" command
2022
 */
2023 2024 2025
static const vshCmdInfo info_node_device_reattach[] = {
    {"help", N_("reattach node device to its device driver")},
    {"desc", N_("Reattach node device to its device driver once released by the domain.")},
2026 2027 2028
    {NULL, NULL}
};

2029 2030 2031

static const vshCmdOptDef opts_node_device_reattach[] = {
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
2032 2033 2034
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2035
static bool
2036
cmdNodeDeviceReAttach(vshControl *ctl, const vshCmd *cmd)
2037
{
2038 2039 2040
    const char *name = NULL;
    virNodeDevicePtr device;
    bool ret = true;
2041

2042
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2043
        return false;
2044
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
2045
        return false;
2046 2047
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
2048
        return false;
2049
    }
2050

2051 2052
    if (virNodeDeviceReAttach(device) == 0) {
        vshPrint(ctl, _("Device %s re-attached\n"), name);
2053
    } else {
2054 2055
        vshError(ctl, _("Failed to re-attach device %s"), name);
        ret = false;
2056
    }
2057 2058 2059
    virNodeDeviceFree(device);
    return ret;
}
2060

2061 2062 2063 2064 2065 2066 2067 2068
/*
 * "nodedev-reset" command
 */
static const vshCmdInfo info_node_device_reset[] = {
    {"help", N_("reset node device")},
    {"desc", N_("Reset node device before or after assigning to a domain.")},
    {NULL, NULL}
};
2069 2070


2071 2072 2073 2074
static const vshCmdOptDef opts_node_device_reset[] = {
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
    {NULL, 0, 0, NULL}
};
2075

2076 2077 2078 2079 2080 2081
static bool
cmdNodeDeviceReset(vshControl *ctl, const vshCmd *cmd)
{
    const char *name = NULL;
    virNodeDevicePtr device;
    bool ret = true;
2082

2083 2084 2085 2086 2087 2088 2089
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
    if (vshCommandOptString(cmd, "device", &name) <= 0)
        return false;
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
        return false;
2090 2091
    }

2092 2093 2094 2095 2096 2097 2098
    if (virNodeDeviceReset(device) == 0) {
        vshPrint(ctl, _("Device %s reset\n"), name);
    } else {
        vshError(ctl, _("Failed to reset device %s"), name);
        ret = false;
    }
    virNodeDeviceFree(device);
2099 2100 2101
    return ret;
}

2102
/*
2103
 * "hostname" command
2104
 */
2105 2106 2107
static const vshCmdInfo info_hostname[] = {
    {"help", N_("print the hypervisor hostname")},
    {"desc", ""},
2108 2109 2110
    {NULL, NULL}
};

E
Eric Blake 已提交
2111
static bool
2112
cmdHostname(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
2113
{
2114
    char *hostname;
2115

2116
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2117
        return false;
2118

2119 2120 2121
    hostname = virConnectGetHostname(ctl->conn);
    if (hostname == NULL) {
        vshError(ctl, "%s", _("failed to get hostname"));
E
Eric Blake 已提交
2122
        return false;
2123
    }
2124

2125 2126
    vshPrint (ctl, "%s\n", hostname);
    VIR_FREE(hostname);
2127

2128 2129
    return true;
}
2130

2131 2132 2133 2134 2135 2136 2137 2138
/*
 * "uri" command
 */
static const vshCmdInfo info_uri[] = {
    {"help", N_("print the hypervisor canonical URI")},
    {"desc", ""},
    {NULL, NULL}
};
2139

2140 2141 2142 2143
static bool
cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *uri;
2144

2145 2146
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
2147

2148 2149 2150 2151
    uri = virConnectGetURI(ctl->conn);
    if (uri == NULL) {
        vshError(ctl, "%s", _("failed to get URI"));
        return false;
2152 2153
    }

2154 2155
    vshPrint(ctl, "%s\n", uri);
    VIR_FREE(uri);
2156

2157 2158
    return true;
}
2159

2160 2161 2162 2163 2164 2165 2166 2167 2168
/*
 * "sysinfo" command
 */
static const vshCmdInfo info_sysinfo[] = {
    {"help", N_("print the hypervisor sysinfo")},
    {"desc",
     N_("output an XML string for the hypervisor sysinfo, if available")},
    {NULL, NULL}
};
2169

2170 2171 2172 2173
static bool
cmdSysinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *sysinfo;
2174

2175 2176
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;
2177

2178 2179 2180 2181
    sysinfo = virConnectGetSysinfo(ctl->conn, 0);
    if (sysinfo == NULL) {
        vshError(ctl, "%s", _("failed to get sysinfo"));
        return false;
2182 2183
    }

2184 2185
    vshPrint(ctl, "%s", sysinfo);
    VIR_FREE(sysinfo);
2186

2187
    return true;
2188 2189
}

2190 2191
/* Common code for the edit / net-edit / pool-edit functions which follow. */
static char *
2192
editWriteToTempFile(vshControl *ctl, const char *doc)
2193 2194 2195 2196 2197 2198 2199
{
    char *ret;
    const char *tmpdir;
    int fd;

    tmpdir = getenv ("TMPDIR");
    if (!tmpdir) tmpdir = "/tmp";
2200 2201 2202 2203
    if (virAsprintf(&ret, "%s/virshXXXXXX.xml", tmpdir) < 0) {
        vshError(ctl, "%s", _("out of memory"));
        return NULL;
    }
2204
    fd = mkstemps(ret, 4);
2205
    if (fd == -1) {
2206
        vshError(ctl, _("mkstemps: failed to create temporary file: %s"),
2207
                 strerror(errno));
2208
        VIR_FREE(ret);
2209 2210 2211
        return NULL;
    }

2212
    if (safewrite(fd, doc, strlen(doc)) == -1) {
2213 2214
        vshError(ctl, _("write: %s: failed to write to temporary file: %s"),
                 ret, strerror(errno));
S
Stefan Berger 已提交
2215
        VIR_FORCE_CLOSE(fd);
2216
        unlink(ret);
2217
        VIR_FREE(ret);
2218 2219
        return NULL;
    }
S
Stefan Berger 已提交
2220
    if (VIR_CLOSE(fd) < 0) {
2221 2222
        vshError(ctl, _("close: %s: failed to write or close temporary file: %s"),
                 ret, strerror(errno));
2223
        unlink(ret);
2224
        VIR_FREE(ret);
2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236
        return NULL;
    }

    /* Temporary filename: caller frees. */
    return ret;
}

/* Characters permitted in $EDITOR environment variable and temp filename. */
#define ACCEPTED_CHARS \
  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/_.:@"

static int
2237
editFile(vshControl *ctl, const char *filename)
2238 2239
{
    const char *editor;
E
Eric Blake 已提交
2240 2241 2242 2243
    virCommandPtr cmd;
    int ret = -1;
    int outfd = STDOUT_FILENO;
    int errfd = STDERR_FILENO;
2244

2245
    editor = getenv("VISUAL");
E
Eric Blake 已提交
2246
    if (!editor)
2247
        editor = getenv("EDITOR");
E
Eric Blake 已提交
2248 2249
    if (!editor)
        editor = "vi"; /* could be cruel & default to ed(1) here */
2250

2251 2252 2253 2254 2255
    /* Check that filename doesn't contain shell meta-characters, and
     * if it does, refuse to run.  Follow the Unix conventions for
     * EDITOR: the user can intentionally specify command options, so
     * we don't protect any shell metacharacters there.  Lots more
     * than virsh will misbehave if EDITOR has bogus contents (which
E
Eric Blake 已提交
2256 2257
     * is why sudo scrubs it by default).  Conversely, if the editor
     * is safe, we can run it directly rather than wasting a shell.
2258
     */
2259 2260
    if (strspn(editor, ACCEPTED_CHARS) != strlen(editor)) {
        if (strspn(filename, ACCEPTED_CHARS) != strlen(filename)) {
E
Eric Blake 已提交
2261 2262 2263 2264 2265 2266 2267 2268 2269 2270
            vshError(ctl,
                     _("%s: temporary filename contains shell meta or other "
                       "unacceptable characters (is $TMPDIR wrong?)"),
                     filename);
            return -1;
        }
        cmd = virCommandNewArgList("sh", "-c", NULL);
        virCommandAddArgFormat(cmd, "%s %s", editor, filename);
    } else {
        cmd = virCommandNewArgList(editor, filename, NULL);
2271 2272
    }

E
Eric Blake 已提交
2273 2274 2275 2276 2277 2278 2279
    virCommandSetInputFD(cmd, STDIN_FILENO);
    virCommandSetOutputFD(cmd, &outfd);
    virCommandSetErrorFD(cmd, &errfd);
    if (virCommandRunAsync(cmd, NULL) < 0 ||
        virCommandWait(cmd, NULL) < 0) {
        virshReportError(ctl);
        goto cleanup;
2280
    }
E
Eric Blake 已提交
2281
    ret = 0;
2282

E
Eric Blake 已提交
2283 2284 2285
cleanup:
    virCommandFree(cmd);
    return ret;
2286 2287 2288
}

static char *
2289
editReadBackFile(vshControl *ctl, const char *filename)
2290 2291 2292
{
    char *ret;

E
Eric Blake 已提交
2293
    if (virFileReadAll(filename, VIRSH_MAX_XML_FILE, &ret) == -1) {
2294
        vshError(ctl,
2295
                 _("%s: failed to read temporary file: %s"),
2296
                 filename, strerror(errno));
2297 2298 2299 2300 2301
        return NULL;
    }
    return ret;
}

2302

P
Paolo Bonzini 已提交
2303 2304 2305 2306
/*
 * "cd" command
 */
static const vshCmdInfo info_cd[] = {
2307 2308
    {"help", N_("change the current directory")},
    {"desc", N_("Change the current directory.")},
P
Paolo Bonzini 已提交
2309 2310 2311 2312
    {NULL, NULL}
};

static const vshCmdOptDef opts_cd[] = {
2313
    {"dir", VSH_OT_DATA, 0, N_("directory to switch to (default: home or else root)")},
P
Paolo Bonzini 已提交
2314 2315 2316
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2317
static bool
2318
cmdCd(vshControl *ctl, const vshCmd *cmd)
P
Paolo Bonzini 已提交
2319
{
2320
    const char *dir = NULL;
2321
    char *dir_malloced = NULL;
E
Eric Blake 已提交
2322
    bool ret = true;
P
Paolo Bonzini 已提交
2323 2324

    if (!ctl->imode) {
2325
        vshError(ctl, "%s", _("cd: command valid only in interactive mode"));
E
Eric Blake 已提交
2326
        return false;
P
Paolo Bonzini 已提交
2327 2328
    }

2329
    if (vshCommandOptString(cmd, "dir", &dir) <= 0) {
2330
        dir = dir_malloced = virGetUserDirectory();
P
Paolo Bonzini 已提交
2331 2332 2333 2334
    }
    if (!dir)
        dir = "/";

P
Phil Petty 已提交
2335
    if (chdir(dir) == -1) {
2336
        vshError(ctl, _("cd: %s: %s"), strerror(errno), dir);
E
Eric Blake 已提交
2337
        ret = false;
P
Paolo Bonzini 已提交
2338 2339
    }

2340
    VIR_FREE(dir_malloced);
P
Phil Petty 已提交
2341
    return ret;
P
Paolo Bonzini 已提交
2342 2343 2344 2345 2346 2347
}

/*
 * "pwd" command
 */
static const vshCmdInfo info_pwd[] = {
2348 2349
    {"help", N_("print the current directory")},
    {"desc", N_("Print the current directory.")},
P
Paolo Bonzini 已提交
2350 2351 2352
    {NULL, NULL}
};

E
Eric Blake 已提交
2353
static bool
P
Paolo Bonzini 已提交
2354 2355 2356
cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *cwd;
2357
    bool ret = true;
P
Paolo Bonzini 已提交
2358

2359 2360
    cwd = getcwd(NULL, 0);
    if (!cwd) {
2361 2362
        vshError(ctl, _("pwd: cannot get current directory: %s"),
                 strerror(errno));
2363 2364
        ret = false;
    } else {
2365
        vshPrint(ctl, _("%s\n"), cwd);
2366 2367
        VIR_FREE(cwd);
    }
P
Paolo Bonzini 已提交
2368

2369
    return ret;
P
Paolo Bonzini 已提交
2370 2371
}

E
Eric Blake 已提交
2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383
/*
 * "echo" command
 */
static const vshCmdInfo info_echo[] = {
    {"help", N_("echo arguments")},
    {"desc", N_("Echo back arguments, possibly with quoting.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_echo[] = {
    {"shell", VSH_OT_BOOL, 0, N_("escape for shell use")},
    {"xml", VSH_OT_BOOL, 0, N_("escape for XML use")},
E
Eric Blake 已提交
2384
    {"str", VSH_OT_ALIAS, 0, "string"},
2385
    {"string", VSH_OT_ARGV, 0, N_("arguments to echo")},
E
Eric Blake 已提交
2386 2387 2388 2389 2390 2391
    {NULL, 0, 0, NULL}
};

/* Exists mainly for debugging virsh, but also handy for adding back
 * quotes for later evaluation.
 */
E
Eric Blake 已提交
2392
static bool
2393
cmdEcho(vshControl *ctl, const vshCmd *cmd)
E
Eric Blake 已提交
2394 2395 2396 2397
{
    bool shell = false;
    bool xml = false;
    int count = 0;
2398
    const vshCmdOpt *opt = NULL;
E
Eric Blake 已提交
2399 2400 2401 2402 2403 2404 2405 2406
    char *arg;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (vshCommandOptBool(cmd, "shell"))
        shell = true;
    if (vshCommandOptBool(cmd, "xml"))
        xml = true;

2407
    while ((opt = vshCommandOptArgv(cmd, opt))) {
2408 2409
        char *str;
        virBuffer xmlbuf = VIR_BUFFER_INITIALIZER;
E
Eric Blake 已提交
2410

2411
        arg = opt->data;
2412

E
Eric Blake 已提交
2413 2414
        if (count)
            virBufferAddChar(&buf, ' ');
2415

E
Eric Blake 已提交
2416
        if (xml) {
2417 2418 2419 2420
            virBufferEscapeString(&xmlbuf, "%s", arg);
            if (virBufferError(&buf)) {
                vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
                return false;
E
Eric Blake 已提交
2421
            }
2422 2423 2424
            str = virBufferContentAndReset(&xmlbuf);
        } else {
            str = vshStrdup(ctl, arg);
E
Eric Blake 已提交
2425
        }
2426 2427 2428 2429 2430

        if (shell)
            virBufferEscapeShell(&buf, str);
        else
            virBufferAdd(&buf, str, -1);
E
Eric Blake 已提交
2431
        count++;
2432
        VIR_FREE(str);
E
Eric Blake 已提交
2433 2434 2435 2436
    }

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
2437
        return false;
E
Eric Blake 已提交
2438 2439 2440 2441 2442
    }
    arg = virBufferContentAndReset(&buf);
    if (arg)
        vshPrint(ctl, "%s", arg);
    VIR_FREE(arg);
E
Eric Blake 已提交
2443
    return true;
E
Eric Blake 已提交
2444 2445
}

K
Karel Zak 已提交
2446 2447 2448
/*
 * "quit" command
 */
2449
static const vshCmdInfo info_quit[] = {
2450
    {"help", N_("quit this interactive terminal")},
2451
    {"desc", ""},
2452
    {NULL, NULL}
K
Karel Zak 已提交
2453 2454
};

E
Eric Blake 已提交
2455
static bool
2456
cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
2457
{
E
Eric Blake 已提交
2458 2459
    ctl->imode = false;
    return true;
K
Karel Zak 已提交
2460 2461
}

2462 2463 2464 2465 2466 2467 2468
/* Helper for snapshot-create and snapshot-create-as */
static bool
vshSnapshotCreate(vshControl *ctl, virDomainPtr dom, const char *buffer,
                  unsigned int flags, const char *from)
{
    bool ret = false;
    virDomainSnapshotPtr snapshot;
2469
    bool halt = false;
2470 2471 2472
    char *doc = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
E
Eric Blake 已提交
2473
    const char *name = NULL;
2474 2475

    snapshot = virDomainSnapshotCreateXML(dom, buffer, flags);
2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499

    /* Emulate --halt on older servers.  */
    if (!snapshot && last_error->code == VIR_ERR_INVALID_ARG &&
        (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT)) {
        int persistent;

        virFreeError(last_error);
        last_error = NULL;
        persistent = virDomainIsPersistent(dom);
        if (persistent < 0) {
            virshReportError(ctl);
            goto cleanup;
        }
        if (!persistent) {
            vshError(ctl, "%s",
                     _("cannot halt after snapshot of transient domain"));
            goto cleanup;
        }
        if (virDomainIsActive(dom) == 1)
            halt = true;
        flags &= ~VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
        snapshot = virDomainSnapshotCreateXML(dom, buffer, flags);
    }

2500 2501 2502
    if (snapshot == NULL)
        goto cleanup;

2503 2504 2505 2506 2507
    if (halt && virDomainDestroy(dom) < 0) {
        virshReportError(ctl);
        goto cleanup;
    }

E
Eric Blake 已提交
2508
    name = virDomainSnapshotGetName(snapshot);
2509
    if (!name) {
E
Eric Blake 已提交
2510
        vshError(ctl, "%s", _("Could not get snapshot name"));
2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529
        goto cleanup;
    }

    if (from)
        vshPrint(ctl, _("Domain snapshot %s created from '%s'"), name, from);
    else
        vshPrint(ctl, _("Domain snapshot %s created"), name);

    ret = true;

cleanup:
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    VIR_FREE(doc);
    return ret;
}

2530 2531 2532 2533
/*
 * "snapshot-create" command
 */
static const vshCmdInfo info_snapshot_create[] = {
E
Eric Blake 已提交
2534 2535
    {"help", N_("Create a snapshot from XML")},
    {"desc", N_("Create a snapshot (disk and RAM) from XML")},
2536 2537 2538 2539 2540 2541
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_create[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"xmlfile", VSH_OT_DATA, 0, N_("domain snapshot XML")},
2542 2543 2544
    {"redefine", VSH_OT_BOOL, 0, N_("redefine metadata for existing snapshot")},
    {"current", VSH_OT_BOOL, 0, N_("with redefine, set current snapshot")},
    {"no-metadata", VSH_OT_BOOL, 0, N_("take snapshot but create no metadata")},
2545
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
2546
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
2547
    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse any existing external files")},
2548
    {"quiesce", VSH_OT_BOOL, 0, N_("quiesce guest's file systems")},
E
Eric Blake 已提交
2549
    {"atomic", VSH_OT_BOOL, 0, N_("require atomic operation")},
2550 2551 2552
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2553
static bool
2554 2555 2556
cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
2557
    bool ret = false;
2558
    const char *from = NULL;
2559
    char *buffer = NULL;
2560 2561 2562 2563 2564 2565 2566 2567
    unsigned int flags = 0;

    if (vshCommandOptBool(cmd, "redefine"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;
    if (vshCommandOptBool(cmd, "current"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;
    if (vshCommandOptBool(cmd, "no-metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
2568 2569
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
2570 2571
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
2572 2573
    if (vshCommandOptBool(cmd, "reuse-external"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT;
2574 2575
    if (vshCommandOptBool(cmd, "quiesce"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE;
E
Eric Blake 已提交
2576 2577
    if (vshCommandOptBool(cmd, "atomic"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC;
2578

2579
    if (!vshConnectionUsability(ctl, ctl->conn))
2580 2581 2582 2583 2584 2585
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

2586
    if (vshCommandOptString(cmd, "xmlfile", &from) <= 0)
E
Eric Blake 已提交
2587
        buffer = vshStrdup(ctl, "<domainsnapshot/>");
2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602
    else {
        if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
            /* we have to report the error here because during cleanup
             * we'll run through virDomainFree(), which loses the
             * last error
             */
            virshReportError(ctl);
            goto cleanup;
        }
    }
    if (buffer == NULL) {
        vshError(ctl, "%s", _("Out of memory"));
        goto cleanup;
    }

2603
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, from);
2604 2605 2606 2607 2608 2609 2610 2611 2612

cleanup:
    VIR_FREE(buffer);
    if (dom)
        virDomainFree(dom);

    return ret;
}

2613 2614 2615
/*
 * "snapshot-create-as" command
 */
2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633
static int
vshParseSnapshotDiskspec(vshControl *ctl, virBufferPtr buf, const char *str)
{
    int ret = -1;
    char *name = NULL;
    char *snapshot = NULL;
    char *driver = NULL;
    char *file = NULL;
    char *spec = vshStrdup(ctl, str);
    char *tmp = spec;
    size_t len = strlen(str);

    if (*str == ',')
        goto cleanup;
    name = tmp;
    while ((tmp = strchr(tmp, ','))) {
        if (tmp[1] == ',') {
            /* Recognize ,, as an escape for a literal comma */
2634
            memmove(&tmp[1], &tmp[2], len - (tmp - spec) - 2 + 1);
2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671
            len--;
            tmp++;
            continue;
        }
        /* Terminate previous string, look for next recognized one */
        *tmp++ = '\0';
        if (!snapshot && STRPREFIX(tmp, "snapshot="))
            snapshot = tmp + strlen("snapshot=");
        else if (!driver && STRPREFIX(tmp, "driver="))
            driver = tmp + strlen("driver=");
        else if (!file && STRPREFIX(tmp, "file="))
            file = tmp + strlen("file=");
        else
            goto cleanup;
    }

    virBufferEscapeString(buf, "    <disk name='%s'", name);
    if (snapshot)
        virBufferAsprintf(buf, " snapshot='%s'", snapshot);
    if (driver || file) {
        virBufferAddLit(buf, ">\n");
        if (driver)
            virBufferAsprintf(buf, "      <driver type='%s'/>\n", driver);
        if (file)
            virBufferEscapeString(buf, "      <source file='%s'/>\n", file);
        virBufferAddLit(buf, "    </disk>\n");
    } else {
        virBufferAddLit(buf, "/>\n");
    }
    ret = 0;
cleanup:
    if (ret < 0)
        vshError(ctl, _("unable to parse diskspec: %s"), str);
    VIR_FREE(spec);
    return ret;
}

2672 2673 2674 2675 2676 2677 2678 2679 2680 2681
static const vshCmdInfo info_snapshot_create_as[] = {
    {"help", N_("Create a snapshot from a set of args")},
    {"desc", N_("Create a snapshot (disk and RAM) from arguments")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_create_as[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"name", VSH_OT_DATA, 0, N_("name of snapshot")},
    {"description", VSH_OT_DATA, 0, N_("description of snapshot")},
E
Eric Blake 已提交
2682
    {"print-xml", VSH_OT_BOOL, 0, N_("print XML document rather than create")},
2683
    {"no-metadata", VSH_OT_BOOL, 0, N_("take snapshot but create no metadata")},
2684
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
2685
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
2686
    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse any existing external files")},
2687
    {"quiesce", VSH_OT_BOOL, 0, N_("quiesce guest's file systems")},
E
Eric Blake 已提交
2688
    {"atomic", VSH_OT_BOOL, 0, N_("require atomic operation")},
2689 2690
    {"diskspec", VSH_OT_ARGV, 0,
     N_("disk attributes: disk[,snapshot=type][,driver=type][,file=name]")},
2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702
    {NULL, 0, 0, NULL}
};

static bool
cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    bool ret = false;
    char *buffer = NULL;
    const char *name = NULL;
    const char *desc = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
2703
    unsigned int flags = 0;
2704
    const vshCmdOpt *opt = NULL;
2705 2706 2707

    if (vshCommandOptBool(cmd, "no-metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
2708 2709
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
2710 2711
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
2712 2713
    if (vshCommandOptBool(cmd, "reuse-external"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT;
2714 2715
    if (vshCommandOptBool(cmd, "quiesce"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE;
E
Eric Blake 已提交
2716 2717
    if (vshCommandOptBool(cmd, "atomic"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC;
2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733

    if (!vshConnectionUsability(ctl, ctl->conn))
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

    if (vshCommandOptString(cmd, "name", &name) < 0 ||
        vshCommandOptString(cmd, "description", &desc) < 0) {
        vshError(ctl, _("argument must not be empty"));
        goto cleanup;
    }

    virBufferAddLit(&buf, "<domainsnapshot>\n");
    if (name)
2734
        virBufferEscapeString(&buf, "  <name>%s</name>\n", name);
2735
    if (desc)
2736
        virBufferEscapeString(&buf, "  <description>%s</description>\n", desc);
2737 2738 2739 2740 2741 2742 2743 2744 2745 2746
    if (vshCommandOptBool(cmd, "diskspec")) {
        virBufferAddLit(&buf, "  <disks>\n");
        while ((opt = vshCommandOptArgv(cmd, opt))) {
            if (vshParseSnapshotDiskspec(ctl, &buf, opt->data) < 0) {
                virBufferFreeAndReset(&buf);
                goto cleanup;
            }
        }
        virBufferAddLit(&buf, "  </disks>\n");
    }
2747 2748 2749 2750 2751 2752 2753 2754
    virBufferAddLit(&buf, "</domainsnapshot>\n");

    buffer = virBufferContentAndReset(&buf);
    if (buffer == NULL) {
        vshError(ctl, "%s", _("Out of memory"));
        goto cleanup;
    }

E
Eric Blake 已提交
2755 2756 2757 2758 2759 2760
    if (vshCommandOptBool(cmd, "print-xml")) {
        vshPrint(ctl, "%s\n",  buffer);
        ret = true;
        goto cleanup;
    }

2761
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, NULL);
2762 2763 2764 2765 2766 2767 2768 2769 2770

cleanup:
    VIR_FREE(buffer);
    if (dom)
        virDomainFree(dom);

    return ret;
}

2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809
/* Helper for resolving {--current | --ARG name} into a snapshot
 * belonging to DOM.  If EXCLUSIVE, fail if both --current and arg are
 * present.  On success, populate *SNAP and *NAME, before returning 0.
 * On failure, return -1 after issuing an error message.  */
static int
vshLookupSnapshot(vshControl *ctl, const vshCmd *cmd,
                  const char *arg, bool exclusive, virDomainPtr dom,
                  virDomainSnapshotPtr *snap, const char **name)
{
    bool current = vshCommandOptBool(cmd, "current");
    const char *snapname = NULL;

    if (vshCommandOptString(cmd, arg, &snapname) < 0) {
        vshError(ctl, _("invalid argument for --%s"), arg);
        return -1;
    }

    if (exclusive && current && snapname) {
        vshError(ctl, _("--%s and --current are mutually exclusive"), arg);
        return -1;
    }

    if (snapname) {
        *snap = virDomainSnapshotLookupByName(dom, snapname, 0);
    } else if (current) {
        *snap = virDomainSnapshotCurrent(dom, 0);
    } else {
        vshError(ctl, _("--%s or --current is required"), arg);
        return -1;
    }
    if (!*snap) {
        virshReportError(ctl);
        return -1;
    }

    *name = virDomainSnapshotGetName(*snap);
    return 0;
}

2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820
/*
 * "snapshot-edit" command
 */
static const vshCmdInfo info_snapshot_edit[] = {
    {"help", N_("edit XML for a snapshot")},
    {"desc", N_("Edit the domain snapshot XML for a named snapshot")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_edit[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
2821
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
2822
    {"current", VSH_OT_BOOL, 0, N_("also set edited snapshot as current")},
2823 2824
    {"rename", VSH_OT_BOOL, 0, N_("allow renaming an existing snapshot")},
    {"clone", VSH_OT_BOOL, 0, N_("allow cloning to new name")},
2825 2826 2827 2828 2829 2830 2831 2832
    {NULL, 0, 0, NULL}
};

static bool
cmdSnapshotEdit(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    virDomainSnapshotPtr snapshot = NULL;
2833
    virDomainSnapshotPtr edited = NULL;
2834
    const char *name;
2835
    const char *edited_name;
2836 2837 2838
    bool ret = false;
    unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
    unsigned int define_flags = VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;
2839 2840 2841 2842 2843 2844 2845 2846
    bool rename_okay = vshCommandOptBool(cmd, "rename");
    bool clone_okay = vshCommandOptBool(cmd, "clone");

    if (rename_okay && clone_okay) {
        vshError(ctl, "%s",
                 _("--rename and --clone are mutually exclusive"));
        return false;
    }
2847

2848 2849
    if (vshCommandOptBool(cmd, "current") &&
        vshCommandOptBool(cmd, "snapshotname"))
2850 2851 2852 2853 2854 2855 2856 2857 2858
        define_flags |= VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;

    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

2859 2860
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", false, dom,
                          &snapshot, &name) < 0)
2861 2862
        goto cleanup;

W
Wen Congyang 已提交
2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881
#define EDIT_GET_XML \
    virDomainSnapshotGetXMLDesc(snapshot, getxml_flags)
#define EDIT_NOT_CHANGED \
    /* Depending on flags, we re-edit even if XML is unchanged.  */ \
    if (!(define_flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT)) {     \
        vshPrint(ctl,                                               \
                 _("Snapshot %s XML configuration not changed.\n"), \
                 name);                                             \
        ret = true;                                                 \
        goto cleanup;                                               \
    }
#define EDIT_DEFINE \
    (strstr(doc, "<state>disk-snapshot</state>") ? \
    define_flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY : 0), \
    edited = virDomainSnapshotCreateXML(dom, doc_edited, define_flags)
#define EDIT_FREE \
    if (edited) \
        virDomainSnapshotFree(edited);
#include "virsh-edit.c"
2882

2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906
    edited_name = virDomainSnapshotGetName(edited);
    if (STREQ(name, edited_name)) {
        vshPrint(ctl, _("Snapshot %s edited.\n"), name);
    } else if (clone_okay) {
        vshPrint(ctl, _("Snapshot %s cloned to %s.\n"), name,
                 edited_name);
    } else {
        unsigned int delete_flags;

        delete_flags = VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY;
        if (virDomainSnapshotDelete(rename_okay ? snapshot : edited,
                                    delete_flags) < 0) {
            virshReportError(ctl);
            vshError(ctl, _("Failed to clean up %s"),
                     rename_okay ? name : edited_name);
            goto cleanup;
        }
        if (!rename_okay) {
            vshError(ctl, _("Must use --rename or --clone to change %s to %s"),
                     name, edited_name);
            goto cleanup;
        }
    }

2907 2908 2909
    ret = true;

cleanup:
2910 2911
    if (edited)
        virDomainSnapshotFree(edited);
2912 2913 2914 2915
    else
        vshError(ctl, _("Failed to update %s"), name);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
2916 2917 2918 2919 2920
    if (dom)
        virDomainFree(dom);
    return ret;
}

2921 2922 2923 2924
/*
 * "snapshot-current" command
 */
static const vshCmdInfo info_snapshot_current[] = {
2925 2926
    {"help", N_("Get or set the current snapshot")},
    {"desc", N_("Get or set the current snapshot")},
2927 2928 2929 2930 2931
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_current[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
2932
    {"name", VSH_OT_BOOL, 0, N_("list the name, rather than the full xml")},
2933 2934
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
2935 2936
    {"snapshotname", VSH_OT_DATA, 0,
     N_("name of existing snapshot to make current")},
2937 2938 2939
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2940
static bool
2941 2942 2943
cmdSnapshotCurrent(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
2944
    bool ret = false;
2945 2946
    int current;
    virDomainSnapshotPtr snapshot = NULL;
2947
    char *xml = NULL;
2948
    const char *snapshotname = NULL;
2949
    unsigned int flags = 0;
2950
    const char *domname;
2951 2952 2953

    if (vshCommandOptBool(cmd, "security-info"))
        flags |= VIR_DOMAIN_XML_SECURE;
2954

2955
    if (!vshConnectionUsability(ctl, ctl->conn))
2956 2957
        goto cleanup;

2958
    dom = vshCommandOptDomain(ctl, cmd, &domname);
2959 2960 2961
    if (dom == NULL)
        goto cleanup;

2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981
    if (vshCommandOptString(cmd, "snapshotname", &snapshotname) < 0) {
        vshError(ctl, _("invalid snapshotname argument '%s'"), snapshotname);
        goto cleanup;
    }
    if (snapshotname) {
        virDomainSnapshotPtr snapshot2 = NULL;
        flags = (VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE |
                 VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT);

        if (vshCommandOptBool(cmd, "name")) {
            vshError(ctl, "%s",
                     _("--name and snapshotname are mutually exclusive"));
            goto cleanup;
        }
        snapshot = virDomainSnapshotLookupByName(dom, snapshotname, 0);
        if (snapshot == NULL)
            goto cleanup;
        xml = virDomainSnapshotGetXMLDesc(snapshot, VIR_DOMAIN_XML_SECURE);
        if (!xml)
            goto cleanup;
2982 2983 2984
        /* strstr is safe here, since xml came from libvirt API and not user */
        if (strstr(xml, "<state>disk-snapshot</state>"))
            flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
2985 2986 2987 2988 2989 2990 2991 2992 2993
        snapshot2 = virDomainSnapshotCreateXML(dom, xml, flags);
        if (snapshot2 == NULL)
            goto cleanup;
        virDomainSnapshotFree(snapshot2);
        vshPrint(ctl, _("Snapshot %s set as current"), snapshotname);
        ret = true;
        goto cleanup;
    }

2994
    current = virDomainHasCurrentSnapshot(dom, 0);
2995 2996 2997 2998
    if (current < 0) {
        goto cleanup;
    } else if (!current) {
        vshError(ctl, _("domain '%s' has no current snapshot"), domname);
2999
        goto cleanup;
3000
    } else {
E
Eric Blake 已提交
3001
        const char *name = NULL;
3002 3003 3004 3005

        if (!(snapshot = virDomainSnapshotCurrent(dom, 0)))
            goto cleanup;

3006
        if (vshCommandOptBool(cmd, "name")) {
E
Eric Blake 已提交
3007
            name = virDomainSnapshotGetName(snapshot);
3008 3009
            if (!name)
                goto cleanup;
E
Eric Blake 已提交
3010 3011 3012 3013
        } else {
            xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
            if (!xml)
                goto cleanup;
3014 3015 3016
        }

        vshPrint(ctl, "%s", name ? name : xml);
3017 3018
    }

E
Eric Blake 已提交
3019
    ret = true;
3020 3021

cleanup:
3022 3023
    if (!ret)
        virshReportError(ctl);
3024
    VIR_FREE(xml);
3025 3026 3027 3028 3029 3030 3031 3032
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

3033
/* Helper function to get the name of a snapshot's parent.  Caller
3034 3035 3036 3037 3038 3039
 * must free the result.  Returns 0 on success (including when it was
 * proven no parent exists), and -1 on failure with error reported
 * (such as no snapshot support or domain deleted in meantime).  */
static int
vshGetSnapshotParent(vshControl *ctl, virDomainSnapshotPtr snapshot,
                     char **parent_name)
3040 3041 3042 3043 3044
{
    virDomainSnapshotPtr parent = NULL;
    char *xml = NULL;
    xmlDocPtr xmldoc = NULL;
    xmlXPathContextPtr ctxt = NULL;
3045 3046 3047
    int ret = -1;

    *parent_name = NULL;
3048 3049

    /* Try new API, since it is faster. */
3050
    if (!ctl->useSnapshotOld) {
3051 3052 3053
        parent = virDomainSnapshotGetParent(snapshot, 0);
        if (parent) {
            /* API works, and virDomainSnapshotGetName will succeed */
3054 3055
            *parent_name = vshStrdup(ctl, virDomainSnapshotGetName(parent));
            ret = 0;
3056 3057 3058 3059
            goto cleanup;
        }
        if (last_error->code == VIR_ERR_NO_DOMAIN_SNAPSHOT) {
            /* API works, and we found a root with no parent */
3060
            ret = 0;
3061 3062 3063
            goto cleanup;
        }
        /* API didn't work, fall back to XML scraping. */
3064
        ctl->useSnapshotOld = true;
3065 3066 3067 3068 3069 3070 3071 3072 3073 3074
    }

    xml = virDomainSnapshotGetXMLDesc(snapshot, 0);
    if (!xml)
        goto cleanup;

    xmldoc = virXMLParseStringCtxt(xml, _("(domain_snapshot)"), &ctxt);
    if (!xmldoc)
        goto cleanup;

3075 3076
    *parent_name = virXPathString("string(/domainsnapshot/parent/name)", ctxt);
    ret = 0;
3077 3078

cleanup:
3079 3080 3081 3082 3083 3084 3085
    if (ret < 0) {
        virshReportError(ctl);
        vshError(ctl, "%s", _("unable to determine if snapshot has parent"));
    } else {
        virFreeError(last_error);
        last_error = NULL;
    }
3086 3087 3088 3089 3090
    if (parent)
        virDomainSnapshotFree(parent);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xmldoc);
    VIR_FREE(xml);
3091
    return ret;
3092 3093
}

E
Eric Blake 已提交
3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216
/*
 * "snapshot-info" command
 */
static const vshCmdInfo info_snapshot_info[] = {
    {"help", N_("snapshot information")},
    {"desc", N_("Returns basic information about a snapshot.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_info[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("info on current snapshot")},
    {NULL, 0, 0, NULL}
};

static bool
cmdSnapshotInfo(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    virDomainSnapshotPtr snapshot = NULL;
    const char *name;
    char *doc = NULL;
    char *tmp;
    char *parent = NULL;
    bool ret = false;
    int count;
    unsigned int flags;
    int current;
    int metadata;

    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        return false;

    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
        goto cleanup;

    vshPrint(ctl, "%-15s %s\n", _("Name:"), name);
    vshPrint(ctl, "%-15s %s\n", _("Domain:"), virDomainGetName(dom));

    /* Determine if snapshot is current; this is useful enough that we
     * attempt a fallback.  */
    current = virDomainSnapshotIsCurrent(snapshot, 0);
    if (current < 0) {
        virDomainSnapshotPtr other = virDomainSnapshotCurrent(dom, 0);

        virResetLastError();
        current = 0;
        if (other) {
            if (STREQ(name, virDomainSnapshotGetName(other)))
                current = 1;
            virDomainSnapshotFree(other);
        }
    }
    vshPrint(ctl, "%-15s %s\n", _("Current:"),
             current > 0 ? _("yes") : _("no"));

    /* Get the XML configuration of the snapshot to determine the
     * state of the machine at the time of the snapshot.  */
    doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
    if (!doc)
        goto cleanup;

    tmp = strstr(doc, "<state>");
    if (!tmp) {
        vshError(ctl, "%s",
                 _("unexpected problem reading snapshot xml"));
        goto cleanup;
    }
    tmp += strlen("<state>");
    vshPrint(ctl, "%-15s %.*s\n", _("State:"),
             (int) (strchr(tmp, '<') - tmp), tmp);

    if (vshGetSnapshotParent(ctl, snapshot, &parent) < 0)
        goto cleanup;
    vshPrint(ctl, "%-15s %s\n", _("Parent:"), parent ? parent : "-");

    /* Children, Descendants.  After this point, the fallback to
     * compute children is too expensive, so we gracefully quit if the
     * APIs don't exist.  */
    if (ctl->useSnapshotOld) {
        ret = true;
        goto cleanup;
    }
    flags = 0;
    count = virDomainSnapshotNumChildren(snapshot, flags);
    if (count < 0)
        goto cleanup;
    vshPrint(ctl, "%-15s %d\n", _("Children:"), count);
    flags = VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
    count = virDomainSnapshotNumChildren(snapshot, flags);
    if (count < 0)
        goto cleanup;
    vshPrint(ctl, "%-15s %d\n", _("Descendants:"), count);

    /* Metadata; the fallback here relies on the fact that metadata
     * used to have an all-or-nothing effect on snapshot count.  */
    metadata = virDomainSnapshotHasMetadata(snapshot, 0);
    if (metadata < 0) {
        metadata = virDomainSnapshotNum(dom,
                                        VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
        virResetLastError();
    }
    if (metadata >= 0)
        vshPrint(ctl, "%-15s %s\n", _("Metadata:"),
                 metadata ? _("yes") : _("no"));

    ret = true;

cleanup:
    VIR_FREE(doc);
    VIR_FREE(parent);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    virDomainFree(dom);
    return ret;
}

3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265
/* Helpers for collecting a list of snapshots.  */
struct vshSnap {
    virDomainSnapshotPtr snap;
    char *parent;
};
struct vshSnapshotList {
    struct vshSnap *snaps;
    int nsnaps;
};
typedef struct vshSnapshotList *vshSnapshotListPtr;

static void
vshSnapshotListFree(vshSnapshotListPtr snaplist)
{
    int i;

    if (!snaplist)
        return;
    if (snaplist->snaps) {
        for (i = 0; i < snaplist->nsnaps; i++) {
            if (snaplist->snaps[i].snap)
                virDomainSnapshotFree(snaplist->snaps[i].snap);
            VIR_FREE(snaplist->snaps[i].parent);
        }
        VIR_FREE(snaplist->snaps);
    }
    VIR_FREE(snaplist);
}

static int
vshSnapSorter(const void *a, const void *b)
{
    const struct vshSnap *sa = a;
    const struct vshSnap *sb = b;

    if (sa->snap && !sb->snap)
        return -1;
    if (!sa->snap)
        return sb->snap != NULL;

    /* User visible sort, so we want locale-specific case comparison.  */
    return strcasecmp(virDomainSnapshotGetName(sa->snap),
                      virDomainSnapshotGetName(sb->snap));
}

/* Compute a list of snapshots from DOM.  If FROM is provided, the
 * list is limited to descendants of the given snapshot.  If FLAGS is
 * given, the list is filtered.  If TREE is specified, then all but
 * FROM or the roots will also have parent information.  */
3266
static vshSnapshotListPtr
3267 3268 3269 3270 3271 3272 3273 3274 3275
vshSnapshotListCollect(vshControl *ctl, virDomainPtr dom,
                       virDomainSnapshotPtr from,
                       unsigned int flags, bool tree)
{
    int i;
    char **names = NULL;
    int count = -1;
    bool descendants = false;
    bool roots = false;
3276
    virDomainSnapshotPtr *snaps;
3277 3278 3279 3280 3281 3282
    vshSnapshotListPtr snaplist = vshMalloc(ctl, sizeof(*snaplist));
    vshSnapshotListPtr ret = NULL;
    const char *fromname = NULL;
    int start_index = -1;
    int deleted = 0;

3283 3284 3285 3286 3287 3288 3289 3290 3291 3292
    /* Try the interface available in 0.9.13 and newer.  */
    if (!ctl->useSnapshotOld) {
        if (from)
            count = virDomainSnapshotListAllChildren(from, &snaps, flags);
        else
            count = virDomainListAllSnapshots(dom, &snaps, flags);
    }
    if (count >= 0) {
        /* When mixing --from and --tree, we also want a copy of from
         * in the list, but with no parent for that one entry.  */
3293 3294
        snaplist->snaps = vshCalloc(ctl, count + (tree && from),
                                    sizeof(*snaplist->snaps));
3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311
        snaplist->nsnaps = count;
        for (i = 0; i < count; i++)
            snaplist->snaps[i].snap = snaps[i];
        VIR_FREE(snaps);
        if (tree) {
            for (i = 0; i < count; i++) {
                if (vshGetSnapshotParent(ctl, snaplist->snaps[i].snap,
                                         &snaplist->snaps[i].parent) < 0)
                    goto cleanup;
            }
            if (from) {
                snaps[snaplist->nsnaps++] = from;
                virDomainSnapshotRef(from);
            }
        }
        goto success;
    }
3312

E
Eric Blake 已提交
3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336
    /* Assume that if we got this far, then the --no-leaves and
     * --no-metadata flags were not supported.  Disable groups that
     * have no impact.  */
    /* XXX should we emulate --no-leaves?  */
    if (flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES &&
        flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES)
        flags &= ~(VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES |
                   VIR_DOMAIN_SNAPSHOT_LIST_LEAVES);
    if (flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA &&
        flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA)
        flags &= ~(VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA |
                   VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
    if (flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA) {
        /* We can emulate --no-metadata if --metadata was supported,
         * since it was an all-or-none attribute on old servers.  */
        count = virDomainSnapshotNum(dom,
                                     VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
        if (count < 0)
            goto cleanup;
        if (count > 0)
            return snaplist;
        flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA;
    }

3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547
    /* This uses the interfaces available in 0.8.0-0.9.6
     * (virDomainSnapshotListNames, global list only) and in
     * 0.9.7-0.9.12 (addition of virDomainSnapshotListChildrenNames
     * for child listing, and new flags), as follows, with [*] by the
     * combinations that need parent info (either for filtering
     * purposes or for the resulting tree listing):
     *                              old               new
     * list                         global as-is      global as-is
     * list --roots                *global + filter   global + flags
     * list --from                 *global + filter   child as-is
     * list --from --descendants   *global + filter   child + flags
     * list --tree                 *global as-is     *global as-is
     * list --tree --from          *global + filter  *child + flags
     *
     * Additionally, when --tree and --from are both used, from is
     * added to the final list as the only element without a parent.
     * Otherwise, --from does not appear in the final list.
     */
    if (from) {
        fromname = virDomainSnapshotGetName(from);
        if (!fromname) {
            vshError(ctl, "%s", _("Could not get snapshot name"));
            goto cleanup;
        }
        descendants = (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) || tree;
        if (tree)
            flags |= VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;

        /* Determine if we can use the new child listing API.  */
        if (ctl->useSnapshotOld ||
            ((count = virDomainSnapshotNumChildren(from, flags)) < 0 &&
             last_error->code == VIR_ERR_NO_SUPPORT)) {
            /* We can emulate --from.  */
            /* XXX can we also emulate --leaves? */
            virFreeError(last_error);
            last_error = NULL;
            ctl->useSnapshotOld = true;
            flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
            goto global;
        }
        if (tree && count >= 0)
            count++;
    } else {
    global:
        /* Global listing (including fallback when --from failed with
         * child listing).  */
        count = virDomainSnapshotNum(dom, flags);

        /* Fall back to simulation if --roots was unsupported. */
        /* XXX can we also emulate --leaves? */
        if (!from && count < 0 && last_error->code == VIR_ERR_INVALID_ARG &&
            (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS)) {
            virFreeError(last_error);
            last_error = NULL;
            roots = true;
            flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
            count = virDomainSnapshotNum(dom, flags);
        }
    }

    if (count < 0) {
        if (!last_error)
            vshError(ctl, _("failed to collect snapshot list"));
        goto cleanup;
    }

    if (!count)
        goto success;

    names = vshCalloc(ctl, sizeof(*names), count);

    /* Now that we have a count, collect the list.  */
    if (from && !ctl->useSnapshotOld) {
        if (tree) {
            if (count)
                count = virDomainSnapshotListChildrenNames(from, names + 1,
                                                           count - 1, flags);
            if (count >= 0) {
                count++;
                names[0] = vshStrdup(ctl, fromname);
            }
        } else {
            count = virDomainSnapshotListChildrenNames(from, names,
                                                       count, flags);
        }
    } else {
        count = virDomainSnapshotListNames(dom, names, count, flags);
    }
    if (count < 0)
        goto cleanup;

    snaplist->snaps = vshCalloc(ctl, sizeof(*snaplist->snaps), count);
    snaplist->nsnaps = count;
    for (i = 0; i < count; i++) {
        snaplist->snaps[i].snap = virDomainSnapshotLookupByName(dom,
                                                                names[i], 0);
        if (!snaplist->snaps[i].snap)
            goto cleanup;
    }

    /* Collect parents when needed.  With the new API, --tree and
     * --from together put from as the first element without a parent;
     * with the old API we still need to do a post-process filtering
     * based on all parent information.  */
    if (tree || (from && ctl->useSnapshotOld) || roots) {
        for (i = (from && !ctl->useSnapshotOld); i < count; i++) {
            if (from && ctl->useSnapshotOld && STREQ(names[i], fromname)) {
                start_index = i;
                if (tree)
                    continue;
            }
            if (vshGetSnapshotParent(ctl, snaplist->snaps[i].snap,
                                     &snaplist->snaps[i].parent) < 0)
                goto cleanup;
            if ((from && ((tree && !snaplist->snaps[i].parent) ||
                          (!descendants &&
                           STRNEQ_NULLABLE(fromname,
                                           snaplist->snaps[i].parent)))) ||
                (roots && snaplist->snaps[i].parent)) {
                virDomainSnapshotFree(snaplist->snaps[i].snap);
                snaplist->snaps[i].snap = NULL;
                VIR_FREE(snaplist->snaps[i].parent);
                deleted++;
            }
        }
    }
    if (tree)
        goto success;

    if (ctl->useSnapshotOld && descendants) {
        bool changed = false;
        bool remaining = false;

        /* Make multiple passes over the list - first pass finds
         * direct children and NULLs out all roots and from, remaining
         * passes NULL out any undecided entry whose parent is not
         * still in list.  We mark known descendants by clearing
         * snaps[i].parents.  Sorry, this is O(n^3) - hope your
         * hierarchy isn't huge.  XXX Is it worth making O(n^2 log n)
         * by using qsort and bsearch?  */
        if (start_index < 0) {
            vshError(ctl, _("snapshot %s disappeared from list"), fromname);
            goto cleanup;
        }
        for (i = 0; i < count; i++) {
            if (i == start_index || !snaplist->snaps[i].parent) {
                VIR_FREE(names[i]);
                virDomainSnapshotFree(snaplist->snaps[i].snap);
                snaplist->snaps[i].snap = NULL;
                VIR_FREE(snaplist->snaps[i].parent);
                deleted++;
            } else if (STREQ(snaplist->snaps[i].parent, fromname)) {
                VIR_FREE(snaplist->snaps[i].parent);
                changed = true;
            } else {
                remaining = true;
            }
        }
        if (!changed) {
            ret = vshMalloc(ctl, sizeof(*snaplist));
            goto cleanup;
        }
        while (changed && remaining) {
            changed = remaining = false;
            for (i = 0; i < count; i++) {
                bool found_parent = false;
                int j;

                if (!names[i] || !snaplist->snaps[i].parent)
                    continue;
                for (j = 0; j < count; j++) {
                    if (!names[j] || i == j)
                        continue;
                    if (STREQ(snaplist->snaps[i].parent, names[j])) {
                        found_parent = true;
                        if (!snaplist->snaps[j].parent)
                            VIR_FREE(snaplist->snaps[i].parent);
                        else
                            remaining = true;
                        break;
                    }
                }
                if (!found_parent) {
                    changed = true;
                    VIR_FREE(names[i]);
                    virDomainSnapshotFree(snaplist->snaps[i].snap);
                    snaplist->snaps[i].snap = NULL;
                    VIR_FREE(snaplist->snaps[i].parent);
                    deleted++;
                }
            }
        }
    }

success:
    qsort(snaplist->snaps, snaplist->nsnaps, sizeof(*snaplist->snaps),
          vshSnapSorter);
    snaplist->nsnaps -= deleted;

    ret = snaplist;
    snaplist = NULL;

cleanup:
    vshSnapshotListFree(snaplist);
    if (names)
        for (i = 0; i < count; i++)
            VIR_FREE(names[i]);
    VIR_FREE(names);
    return ret;
}

3548 3549 3550 3551 3552 3553 3554 3555 3556
static const char *
vshSnapshotListLookup(int id, bool parent, void *opaque)
{
    vshSnapshotListPtr snaplist = opaque;
    if (parent)
        return snaplist->snaps[id].parent;
    return virDomainSnapshotGetName(snaplist->snaps[id].snap);
}

3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567
/*
 * "snapshot-list" command
 */
static const vshCmdInfo info_snapshot_list[] = {
    {"help", N_("List snapshots for a domain")},
    {"desc", N_("Snapshot List")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_list[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3568
    {"parent", VSH_OT_BOOL, 0, N_("add a column showing parent snapshot")},
3569
    {"roots", VSH_OT_BOOL, 0, N_("list only snapshots without parents")},
3570
    {"leaves", VSH_OT_BOOL, 0, N_("list only snapshots without children")},
E
Eric Blake 已提交
3571 3572
    {"no-leaves", VSH_OT_BOOL, 0,
     N_("list only snapshots that are not leaves (with children)")},
3573 3574
    {"metadata", VSH_OT_BOOL, 0,
     N_("list only snapshots that have metadata that would prevent undefine")},
E
Eric Blake 已提交
3575 3576
    {"no-metadata", VSH_OT_BOOL, 0,
     N_("list only snapshots that have no metadata managed by libvirt")},
3577
    {"tree", VSH_OT_BOOL, 0, N_("list snapshots in a tree")},
3578
    {"from", VSH_OT_DATA, 0, N_("limit list to children of given snapshot")},
3579 3580
    {"current", VSH_OT_BOOL, 0,
     N_("limit list to children of current snapshot")},
3581
    {"descendants", VSH_OT_BOOL, 0, N_("with --from, list all descendants")},
3582 3583 3584
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3585
static bool
3586 3587 3588
cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
3589
    bool ret = false;
3590
    unsigned int flags = 0;
3591
    bool show_parent = false;
3592 3593 3594 3595 3596 3597
    int i;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    char *doc = NULL;
    virDomainSnapshotPtr snapshot = NULL;
    char *state = NULL;
3598
    char *parent = NULL;
3599 3600
    long long creation_longlong;
    time_t creation_time_t;
3601 3602
    char timestr[100];
    struct tm time_info;
3603
    bool tree = vshCommandOptBool(cmd, "tree");
3604
    bool leaves = vshCommandOptBool(cmd, "leaves");
E
Eric Blake 已提交
3605
    bool no_leaves = vshCommandOptBool(cmd, "no-leaves");
3606 3607
    const char *from = NULL;
    virDomainSnapshotPtr start = NULL;
3608
    vshSnapshotListPtr snaplist = NULL;
3609

3610 3611 3612 3613 3614 3615 3616 3617 3618 3619
    if (!vshConnectionUsability(ctl, ctl->conn))
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

    if ((vshCommandOptBool(cmd, "from") ||
         vshCommandOptBool(cmd, "current")) &&
        vshLookupSnapshot(ctl, cmd, "from", true, dom, &start, &from) < 0)
3620
        goto cleanup;
3621

3622
    if (vshCommandOptBool(cmd, "parent")) {
3623 3624
        if (vshCommandOptBool(cmd, "roots")) {
            vshError(ctl, "%s",
3625
                     _("--parent and --roots are mutually exclusive"));
3626
            goto cleanup;
3627
        }
3628 3629
        if (tree) {
            vshError(ctl, "%s",
3630
                     _("--parent and --tree are mutually exclusive"));
3631
            goto cleanup;
3632
        }
3633
        show_parent = true;
3634
    } else if (vshCommandOptBool(cmd, "roots")) {
3635 3636
        if (tree) {
            vshError(ctl, "%s",
3637
                     _("--roots and --tree are mutually exclusive"));
3638
            goto cleanup;
3639
        }
3640 3641 3642
        if (from) {
            vshError(ctl, "%s",
                     _("--roots and --from are mutually exclusive"));
3643
            goto cleanup;
3644
        }
3645 3646
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
    }
3647 3648 3649 3650
    if (leaves) {
        if (tree) {
            vshError(ctl, "%s",
                     _("--leaves and --tree are mutually exclusive"));
3651
            goto cleanup;
3652 3653 3654
        }
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_LEAVES;
    }
E
Eric Blake 已提交
3655 3656 3657 3658 3659 3660 3661 3662
    if (no_leaves) {
        if (tree) {
            vshError(ctl, "%s",
                     _("--no-leaves and --tree are mutually exclusive"));
            goto cleanup;
        }
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES;
    }
3663 3664 3665

    if (vshCommandOptBool(cmd, "metadata")) {
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_METADATA;
3666
    }
E
Eric Blake 已提交
3667 3668 3669
    if (vshCommandOptBool(cmd, "no-metadata")) {
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA;
    }
3670

3671 3672 3673 3674 3675
    if (vshCommandOptBool(cmd, "descendants")) {
        if (!from) {
            vshError(ctl, "%s",
                     _("--descendants requires either --from or --current"));
            goto cleanup;
3676
        }
3677
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
3678 3679
    }

3680 3681
    if ((snaplist = vshSnapshotListCollect(ctl, dom, start, flags,
                                           tree)) == NULL)
3682 3683
        goto cleanup;

3684
    if (!tree) {
3685
        if (show_parent)
3686 3687 3688 3689 3690 3691
            vshPrintExtra(ctl, " %-20s %-25s %-15s %s",
                          _("Name"), _("Creation Time"), _("State"),
                          _("Parent"));
        else
            vshPrintExtra(ctl, " %-20s %-25s %s",
                          _("Name"), _("Creation Time"), _("State"));
E
Eric Blake 已提交
3692 3693
        vshPrintExtra(ctl, "\n"
"------------------------------------------------------------\n");
3694
    }
3695

3696
    if (!snaplist->nsnaps) {
3697 3698 3699
        ret = true;
        goto cleanup;
    }
3700

3701
    if (tree) {
3702 3703 3704 3705
        for (i = 0; i < snaplist->nsnaps; i++) {
            if (!snaplist->snaps[i].parent &&
                vshTreePrint(ctl, vshSnapshotListLookup, snaplist,
                             snaplist->nsnaps, i) < 0)
3706
                goto cleanup;
3707 3708 3709
        }
        ret = true;
        goto cleanup;
E
Eric Blake 已提交
3710 3711
    }

3712 3713
    for (i = 0; i < snaplist->nsnaps; i++) {
        const char *name;
3714

E
Eric Blake 已提交
3715 3716 3717 3718 3719 3720
        /* free up memory from previous iterations of the loop */
        VIR_FREE(parent);
        VIR_FREE(state);
        xmlXPathFreeContext(ctxt);
        xmlFreeDoc(xml);
        VIR_FREE(doc);
3721

3722 3723 3724
        snapshot = snaplist->snaps[i].snap;
        name = virDomainSnapshotGetName(snapshot);
        assert(name);
3725

E
Eric Blake 已提交
3726 3727 3728
        doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
        if (!doc)
            continue;
3729

E
Eric Blake 已提交
3730 3731 3732
        xml = virXMLParseStringCtxt(doc, _("(domain_snapshot)"), &ctxt);
        if (!xml)
            continue;
3733

3734
        if (show_parent)
E
Eric Blake 已提交
3735 3736
            parent = virXPathString("string(/domainsnapshot/parent/name)",
                                    ctxt);
3737

E
Eric Blake 已提交
3738 3739 3740 3741 3742 3743 3744 3745 3746 3747
        state = virXPathString("string(/domainsnapshot/state)", ctxt);
        if (state == NULL)
            continue;
        if (virXPathLongLong("string(/domainsnapshot/creationTime)", ctxt,
                             &creation_longlong) < 0)
            continue;
        creation_time_t = creation_longlong;
        if (creation_time_t != creation_longlong) {
            vshError(ctl, "%s", _("time_t overflow"));
            continue;
3748
        }
E
Eric Blake 已提交
3749 3750 3751 3752 3753 3754
        localtime_r(&creation_time_t, &time_info);
        strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S %z",
                 &time_info);

        if (parent)
            vshPrint(ctl, " %-20s %-25s %-15s %s\n",
3755
                     name, timestr, state, parent);
E
Eric Blake 已提交
3756
        else
3757
            vshPrint(ctl, " %-20s %-25s %s\n", name, timestr, state);
3758 3759
    }

E
Eric Blake 已提交
3760
    ret = true;
3761 3762 3763

cleanup:
    /* this frees up memory from the last iteration of the loop */
3764
    vshSnapshotListFree(snaplist);
3765
    VIR_FREE(parent);
3766
    VIR_FREE(state);
3767 3768
    if (start)
        virDomainSnapshotFree(start);
3769
    xmlXPathFreeContext(ctxt);
3770
    xmlFreeDoc(xml);
3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789
    VIR_FREE(doc);
    if (dom)
        virDomainFree(dom);

    return ret;
}

/*
 * "snapshot-dumpxml" command
 */
static const vshCmdInfo info_snapshot_dumpxml[] = {
    {"help", N_("Dump XML for a domain snapshot")},
    {"desc", N_("Snapshot Dump XML")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_dumpxml[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"snapshotname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("snapshot name")},
3790 3791
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
3792 3793 3794
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3795
static bool
3796 3797 3798
cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
3799
    bool ret = false;
3800
    const char *name = NULL;
3801 3802
    virDomainSnapshotPtr snapshot = NULL;
    char *xml = NULL;
3803 3804 3805 3806
    unsigned int flags = 0;

    if (vshCommandOptBool(cmd, "security-info"))
        flags |= VIR_DOMAIN_XML_SECURE;
3807

3808
    if (!vshConnectionUsability(ctl, ctl->conn))
3809 3810 3811 3812 3813 3814
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

3815
    if (vshCommandOptString(cmd, "snapshotname", &name) <= 0)
3816 3817 3818 3819 3820 3821
        goto cleanup;

    snapshot = virDomainSnapshotLookupByName(dom, name, 0);
    if (snapshot == NULL)
        goto cleanup;

3822
    xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
3823 3824 3825
    if (!xml)
        goto cleanup;

3826
    vshPrint(ctl, "%s", xml);
3827

E
Eric Blake 已提交
3828
    ret = true;
3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839

cleanup:
    VIR_FREE(xml);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

E
Eric Blake 已提交
3840 3841 3842 3843
/*
 * "snapshot-parent" command
 */
static const vshCmdInfo info_snapshot_parent[] = {
E
Eric Blake 已提交
3844
    {"help", N_("Get the name of the parent of a snapshot")},
E
Eric Blake 已提交
3845 3846 3847 3848 3849 3850
    {"desc", N_("Extract the snapshot's parent, if any")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_parent[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3851 3852
    {"snapshotname", VSH_OT_DATA, 0, N_("find parent of snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("find parent of current snapshot")},
E
Eric Blake 已提交
3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871
    {NULL, 0, 0, NULL}
};

static bool
cmdSnapshotParent(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    bool ret = false;
    const char *name = NULL;
    virDomainSnapshotPtr snapshot = NULL;
    char *parent = NULL;

    if (!vshConnectionUsability(ctl, ctl->conn))
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

3872 3873
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
E
Eric Blake 已提交
3874 3875
        goto cleanup;

3876
    if (vshGetSnapshotParent(ctl, snapshot, &parent) < 0)
E
Eric Blake 已提交
3877
        goto cleanup;
3878 3879 3880 3881
    if (!parent) {
        vshError(ctl, _("snapshot '%s' has no parent"), name);
        goto cleanup;
    }
E
Eric Blake 已提交
3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896

    vshPrint(ctl, "%s", parent);

    ret = true;

cleanup:
    VIR_FREE(parent);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

3897
/*
3898
 * "snapshot-revert" command
3899
 */
3900
static const vshCmdInfo info_snapshot_revert[] = {
3901 3902 3903 3904 3905
    {"help", N_("Revert a domain to a snapshot")},
    {"desc", N_("Revert domain to snapshot")},
    {NULL, NULL}
};

3906
static const vshCmdOptDef opts_snapshot_revert[] = {
3907
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3908 3909
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("revert to current snapshot")},
3910 3911
    {"running", VSH_OT_BOOL, 0, N_("after reverting, change state to running")},
    {"paused", VSH_OT_BOOL, 0, N_("after reverting, change state to paused")},
E
Eric Blake 已提交
3912
    {"force", VSH_OT_BOOL, 0, N_("try harder on risky reverts")},
3913 3914 3915
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3916
static bool
3917
cmdDomainSnapshotRevert(vshControl *ctl, const vshCmd *cmd)
3918 3919
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
3920
    bool ret = false;
3921
    const char *name = NULL;
3922
    virDomainSnapshotPtr snapshot = NULL;
3923
    unsigned int flags = 0;
E
Eric Blake 已提交
3924 3925
    bool force = false;
    int result;
3926 3927 3928 3929 3930

    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED;
E
Eric Blake 已提交
3931 3932 3933 3934 3935 3936
    /* We want virsh snapshot-revert --force to work even when talking
     * to older servers that did the unsafe revert by default but
     * reject the flag, so we probe without the flag, and only use it
     * when the error says it will make a difference.  */
    if (vshCommandOptBool(cmd, "force"))
        force = true;
3937

3938
    if (!vshConnectionUsability(ctl, ctl->conn))
3939 3940 3941 3942 3943 3944
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

3945 3946
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
3947 3948
        goto cleanup;

E
Eric Blake 已提交
3949 3950 3951 3952 3953 3954 3955 3956 3957
    result = virDomainRevertToSnapshot(snapshot, flags);
    if (result < 0 && force &&
        last_error->code == VIR_ERR_SNAPSHOT_REVERT_RISKY) {
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_FORCE;
        virFreeError(last_error);
        last_error = NULL;
        result = virDomainRevertToSnapshot(snapshot, flags);
    }
    if (result < 0)
3958 3959
        goto cleanup;

E
Eric Blake 已提交
3960
    ret = true;
3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981

cleanup:
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

/*
 * "snapshot-delete" command
 */
static const vshCmdInfo info_snapshot_delete[] = {
    {"help", N_("Delete a domain snapshot")},
    {"desc", N_("Snapshot Delete")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_delete[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3982 3983
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("delete current snapshot")},
3984
    {"children", VSH_OT_BOOL, 0, N_("delete snapshot and all children")},
3985 3986 3987
    {"children-only", VSH_OT_BOOL, 0, N_("delete children but not snapshot")},
    {"metadata", VSH_OT_BOOL, 0,
     N_("delete only libvirt metadata, leaving snapshot contents behind")},
3988 3989 3990
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3991
static bool
3992 3993 3994
cmdSnapshotDelete(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
3995
    bool ret = false;
3996
    const char *name = NULL;
3997 3998 3999
    virDomainSnapshotPtr snapshot = NULL;
    unsigned int flags = 0;

4000
    if (!vshConnectionUsability(ctl, ctl->conn))
4001 4002 4003 4004 4005 4006
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

4007 4008
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
4009 4010 4011 4012
        goto cleanup;

    if (vshCommandOptBool(cmd, "children"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN;
4013 4014 4015 4016
    if (vshCommandOptBool(cmd, "children-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY;
    if (vshCommandOptBool(cmd, "metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY;
4017

4018 4019 4020
    /* XXX If we wanted, we could emulate DELETE_CHILDREN_ONLY even on
     * older servers that reject the flag, by manually computing the
     * list of descendants.  But that's a lot of code to maintain.  */
4021
    if (virDomainSnapshotDelete(snapshot, flags) == 0) {
4022 4023 4024 4025
        if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY)
            vshPrint(ctl, _("Domain snapshot %s children deleted\n"), name);
        else
            vshPrint(ctl, _("Domain snapshot %s deleted\n"), name);
4026 4027
    } else {
        vshError(ctl, _("Failed to delete snapshot %s"), name);
4028
        goto cleanup;
4029
    }
4030

E
Eric Blake 已提交
4031
    ret = true;
4032 4033 4034 4035 4036 4037 4038 4039 4040 4041

cleanup:
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

4042 4043 4044 4045
/*
 * "qemu-monitor-command" command
 */
static const vshCmdInfo info_qemu_monitor_command[] = {
4046 4047
    {"help", N_("QEMU Monitor Command")},
    {"desc", N_("QEMU Monitor Command")},
4048 4049 4050 4051 4052
    {NULL, NULL}
};

static const vshCmdOptDef opts_qemu_monitor_command[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4053
    {"hmp", VSH_OT_BOOL, 0, N_("command is in human monitor protocol")},
4054
    {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")},
4055 4056 4057
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4058
static bool
4059 4060 4061
cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
4062
    bool ret = false;
4063
    char *monitor_cmd = NULL;
4064
    char *result = NULL;
4065
    unsigned int flags = 0;
4066 4067 4068
    const vshCmdOpt *opt = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    bool pad = false;
4069 4070 4071 4072 4073 4074 4075 4076

    if (!vshConnectionUsability(ctl, ctl->conn))
        goto cleanup;

    dom = vshCommandOptDomain(ctl, cmd, NULL);
    if (dom == NULL)
        goto cleanup;

4077 4078 4079 4080 4081 4082 4083 4084
    while ((opt = vshCommandOptArgv(cmd, opt))) {
        if (pad)
            virBufferAddChar(&buf, ' ');
        pad = true;
        virBufferAdd(&buf, opt->data, -1);
    }
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to collect command"));
4085 4086
        goto cleanup;
    }
4087
    monitor_cmd = virBufferContentAndReset(&buf);
4088

4089 4090 4091 4092
    if (vshCommandOptBool(cmd, "hmp"))
        flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP;

    if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0)
4093 4094 4095 4096
        goto cleanup;

    printf("%s\n", result);

E
Eric Blake 已提交
4097
    ret = true;
4098 4099 4100

cleanup:
    VIR_FREE(result);
4101
    VIR_FREE(monitor_cmd);
4102 4103 4104 4105 4106 4107
    if (dom)
        virDomainFree(dom);

    return ret;
}

4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127
/*
 * "qemu-attach" command
 */
static const vshCmdInfo info_qemu_attach[] = {
    {"help", N_("QEMU Attach")},
    {"desc", N_("QEMU Attach")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_qemu_attach[] = {
    {"pid", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pid")},
    {NULL, 0, 0, NULL}
};

static bool
cmdQemuAttach(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    bool ret = false;
    unsigned int flags = 0;
4128
    unsigned int pid_value; /* API uses unsigned int, not pid_t */
4129 4130 4131 4132

    if (!vshConnectionUsability(ctl, ctl->conn))
        goto cleanup;

4133
    if (vshCommandOptUInt(cmd, "pid", &pid_value) <= 0) {
4134 4135 4136 4137
        vshError(ctl, "%s", _("missing pid value"));
        goto cleanup;
    }

4138
    if (!(dom = virDomainQemuAttach(ctl->conn, pid_value, flags)))
4139 4140 4141 4142
        goto cleanup;

    if (dom != NULL) {
        vshPrint(ctl, _("Domain %s attached to pid %u\n"),
4143
                 virDomainGetName(dom), pid_value);
4144 4145 4146
        virDomainFree(dom);
        ret = true;
    } else {
4147
        vshError(ctl, _("Failed to attach to pid %u"), pid_value);
4148 4149 4150 4151 4152 4153
    }

cleanup:
    return ret;
}

4154 4155 4156 4157 4158 4159 4160 4161
/* ---------------
 * Utils for work with command definition
 * ---------------
 */
static const char *
vshCmddefGetInfo(const vshCmdDef * cmd, const char *name)
{
    const vshCmdInfo *info;
4162

4163 4164 4165 4166 4167 4168
    for (info = cmd->info; info && info->name; info++) {
        if (STREQ(info->name, name))
            return info->data;
    }
    return NULL;
}
4169

4170 4171 4172 4173 4174 4175 4176
/* 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)
{
    int i;
    bool optional = false;
4177

4178 4179
    *opts_need_arg = 0;
    *opts_required = 0;
4180

4181 4182
    if (!cmd->opts)
        return 0;
4183

4184 4185
    for (i = 0; cmd->opts[i].name; i++) {
        const vshCmdOptDef *opt = &cmd->opts[i];
4186 4187 4188 4189

        if (i > 31)
            return -1; /* too many options */
        if (opt->type == VSH_OT_BOOL) {
E
Eric Blake 已提交
4190
            if (opt->flags & VSH_OFLAG_REQ)
4191 4192 4193
                return -1; /* bool options can't be mandatory */
            continue;
        }
E
Eric Blake 已提交
4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205
        if (opt->type == VSH_OT_ALIAS) {
            int j;
            if (opt->flags || !opt->help)
                return -1; /* alias options are tracked by the original name */
            for (j = i + 1; cmd->opts[j].name; j++) {
                if (STREQ(opt->help, cmd->opts[j].name))
                    break;
            }
            if (!cmd->opts[j].name)
                return -1; /* alias option must map to a later option name */
            continue;
        }
E
Eric Blake 已提交
4206 4207
        if (opt->flags & VSH_OFLAG_REQ_OPT) {
            if (opt->flags & VSH_OFLAG_REQ)
L
Lai Jiangshan 已提交
4208 4209 4210 4211
                *opts_required |= 1 << i;
            continue;
        }

4212
        *opts_need_arg |= 1 << i;
E
Eric Blake 已提交
4213
        if (opt->flags & VSH_OFLAG_REQ) {
4214 4215 4216 4217 4218 4219
            if (optional)
                return -1; /* mandatory options must be listed first */
            *opts_required |= 1 << i;
        } else {
            optional = true;
        }
4220 4221 4222

        if (opt->type == VSH_OT_ARGV && cmd->opts[i + 1].name)
            return -1; /* argv option must be listed last */
4223 4224 4225 4226
    }
    return 0;
}

4227
static const vshCmdOptDef *
4228
vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
4229
                   uint32_t *opts_seen, int *opt_index)
4230
{
4231 4232 4233 4234
    int i;

    for (i = 0; cmd->opts && cmd->opts[i].name; i++) {
        const vshCmdOptDef *opt = &cmd->opts[i];
4235

4236
        if (STREQ(opt->name, name)) {
E
Eric Blake 已提交
4237 4238 4239 4240
            if (opt->type == VSH_OT_ALIAS) {
                name = opt->help;
                continue;
            }
4241
            if ((*opts_seen & (1 << i)) && opt->type != VSH_OT_ARGV) {
4242 4243 4244
                vshError(ctl, _("option --%s already seen"), name);
                return NULL;
            }
4245 4246
            *opts_seen |= 1 << i;
            *opt_index = i;
K
Karel Zak 已提交
4247
            return opt;
4248 4249 4250 4251 4252
        }
    }

    vshError(ctl, _("command '%s' doesn't support option --%s"),
             cmd->name, name);
K
Karel Zak 已提交
4253 4254 4255
    return NULL;
}

4256
static const vshCmdOptDef *
4257 4258
vshCmddefGetData(const vshCmdDef *cmd, uint32_t *opts_need_arg,
                 uint32_t *opts_seen)
4259
{
4260
    int i;
4261
    const vshCmdOptDef *opt;
K
Karel Zak 已提交
4262

4263 4264 4265 4266
    if (!*opts_need_arg)
        return NULL;

    /* Grab least-significant set bit */
E
Eric Blake 已提交
4267
    i = ffs(*opts_need_arg) - 1;
4268
    opt = &cmd->opts[i];
4269
    if (opt->type != VSH_OT_ARGV)
4270
        *opts_need_arg &= ~(1 << i);
4271
    *opts_seen |= 1 << i;
4272
    return opt;
K
Karel Zak 已提交
4273 4274
}

4275 4276 4277
/*
 * Checks for required options
 */
4278
static int
4279 4280
vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required,
                    uint32_t opts_seen)
4281
{
4282
    const vshCmdDef *def = cmd->def;
4283 4284 4285 4286 4287 4288 4289 4290 4291
    int i;

    opts_required &= ~opts_seen;
    if (!opts_required)
        return 0;

    for (i = 0; def->opts[i].name; i++) {
        if (opts_required & (1 << i)) {
            const vshCmdOptDef *opt = &def->opts[i];
4292

4293
            vshError(ctl,
4294
                     opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ?
4295 4296 4297
                     _("command '%s' requires <%s> option") :
                     _("command '%s' requires --%s option"),
                     def->name, opt->name);
4298 4299
        }
    }
4300
    return -1;
4301 4302
}

4303
static const vshCmdDef *
4304 4305
vshCmddefSearch(const char *cmdname)
{
4306
    const vshCmdGrp *g;
4307
    const vshCmdDef *c;
4308

4309 4310
    for (g = cmdGroups; g->name; g++) {
        for (c = g->commands; c->name; c++) {
4311
            if (STREQ(c->name, cmdname))
4312 4313 4314 4315
                return c;
        }
    }

K
Karel Zak 已提交
4316 4317 4318
    return NULL;
}

4319 4320 4321 4322 4323 4324
static const vshCmdGrp *
vshCmdGrpSearch(const char *grpname)
{
    const vshCmdGrp *g;

    for (g = cmdGroups; g->name; g++) {
4325
        if (STREQ(g->name, grpname) || STREQ(g->keyword, grpname))
4326 4327 4328 4329 4330 4331
            return g;
    }

    return NULL;
}

E
Eric Blake 已提交
4332
static bool
4333 4334 4335 4336 4337 4338 4339
vshCmdGrpHelp(vshControl *ctl, const char *grpname)
{
    const vshCmdGrp *grp = vshCmdGrpSearch(grpname);
    const vshCmdDef *cmd = NULL;

    if (!grp) {
        vshError(ctl, _("command group '%s' doesn't exist"), grpname);
E
Eric Blake 已提交
4340
        return false;
4341 4342 4343 4344 4345 4346 4347 4348 4349 4350
    } else {
        vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name,
                 grp->keyword);

        for (cmd = grp->commands; cmd->name; cmd++) {
            vshPrint(ctl, "    %-30s %s\n", cmd->name,
                     _(vshCmddefGetInfo(cmd, "help")));
        }
    }

E
Eric Blake 已提交
4351
    return true;
4352 4353
}

E
Eric Blake 已提交
4354
static bool
4355
vshCmddefHelp(vshControl *ctl, const char *cmdname)
4356
{
4357
    const vshCmdDef *def = vshCmddefSearch(cmdname);
4358

K
Karel Zak 已提交
4359
    if (!def) {
4360
        vshError(ctl, _("command '%s' doesn't exist"), cmdname);
E
Eric Blake 已提交
4361
        return false;
4362
    } else {
E
Eric Blake 已提交
4363 4364
        /* Don't translate desc if it is "".  */
        const char *desc = vshCmddefGetInfo(def, "desc");
E
Eric Blake 已提交
4365
        const char *help = _(vshCmddefGetInfo(def, "help"));
4366
        char buf[256];
4367 4368
        uint32_t opts_need_arg;
        uint32_t opts_required;
4369
        bool shortopt = false; /* true if 'arg' works instead of '--opt arg' */
4370 4371 4372 4373

        if (vshCmddefOptParse(def, &opts_need_arg, &opts_required)) {
            vshError(ctl, _("internal error: bad options in command: '%s'"),
                     def->name);
E
Eric Blake 已提交
4374
            return false;
4375
        }
K
Karel Zak 已提交
4376

4377
        fputs(_("  NAME\n"), stdout);
4378 4379
        fprintf(stdout, "    %s - %s\n", def->name, help);

4380 4381 4382 4383 4384
        fputs(_("\n  SYNOPSIS\n"), stdout);
        fprintf(stdout, "    %s", def->name);
        if (def->opts) {
            const vshCmdOptDef *opt;
            for (opt = def->opts; opt->name; opt++) {
4385
                const char *fmt = "%s";
4386 4387
                switch (opt->type) {
                case VSH_OT_BOOL:
4388
                    fmt = "[--%s]";
4389 4390
                    break;
                case VSH_OT_INT:
E
Eric Blake 已提交
4391
                    /* xgettext:c-format */
E
Eric Blake 已提交
4392
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>"
4393
                           : _("[--%s <number>]"));
4394 4395
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
4396 4397
                    break;
                case VSH_OT_STRING:
E
Eric Blake 已提交
4398 4399
                    /* xgettext:c-format */
                    fmt = _("[--%s <string>]");
4400 4401
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
4402 4403
                    break;
                case VSH_OT_DATA:
E
Eric Blake 已提交
4404
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>" : "[<%s>]");
4405 4406
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
4407 4408 4409
                    break;
                case VSH_OT_ARGV:
                    /* xgettext:c-format */
4410 4411 4412 4413 4414 4415 4416 4417
                    if (shortopt) {
                        fmt = (opt->flags & VSH_OFLAG_REQ)
                            ? _("{[--%s] <string>}...")
                            : _("[[--%s] <string>]...");
                    } else {
                        fmt = (opt->flags & VSH_OFLAG_REQ) ? _("<%s>...")
                            : _("[<%s>]...");
                    }
4418
                    break;
E
Eric Blake 已提交
4419 4420 4421
                case VSH_OT_ALIAS:
                    /* aliases are intentionally undocumented */
                    continue;
4422
                default:
4423
                    assert(0);
4424
                }
4425
                fputc(' ', stdout);
E
Eric Blake 已提交
4426
                fprintf(stdout, fmt, opt->name);
4427
            }
K
Karel Zak 已提交
4428
        }
4429 4430 4431
        fputc('\n', stdout);

        if (desc[0]) {
4432
            /* Print the description only if it's not empty.  */
4433
            fputs(_("\n  DESCRIPTION\n"), stdout);
E
Eric Blake 已提交
4434
            fprintf(stdout, "    %s\n", _(desc));
K
Karel Zak 已提交
4435
        }
4436

K
Karel Zak 已提交
4437
        if (def->opts) {
4438
            const vshCmdOptDef *opt;
4439
            fputs(_("\n  OPTIONS\n"), stdout);
4440
            for (opt = def->opts; opt->name; opt++) {
4441 4442
                switch (opt->type) {
                case VSH_OT_BOOL:
K
Karel Zak 已提交
4443
                    snprintf(buf, sizeof(buf), "--%s", opt->name);
4444 4445
                    break;
                case VSH_OT_INT:
4446
                    snprintf(buf, sizeof(buf),
E
Eric Blake 已提交
4447
                             (opt->flags & VSH_OFLAG_REQ) ? _("[--%s] <number>")
4448
                             : _("--%s <number>"), opt->name);
4449 4450
                    break;
                case VSH_OT_STRING:
4451
                    /* OT_STRING should never be VSH_OFLAG_REQ */
4452
                    snprintf(buf, sizeof(buf), _("--%s <string>"), opt->name);
4453 4454
                    break;
                case VSH_OT_DATA:
4455 4456
                    snprintf(buf, sizeof(buf), _("[--%s] <string>"),
                             opt->name);
4457 4458
                    break;
                case VSH_OT_ARGV:
4459 4460 4461
                    snprintf(buf, sizeof(buf),
                             shortopt ? _("[--%s] <string>") : _("<%s>"),
                             opt->name);
4462
                    break;
E
Eric Blake 已提交
4463 4464
                case VSH_OT_ALIAS:
                    continue;
4465 4466 4467
                default:
                    assert(0);
                }
4468

E
Eric Blake 已提交
4469
                fprintf(stdout, "    %-15s  %s\n", buf, _(opt->help));
4470
            }
K
Karel Zak 已提交
4471 4472 4473
        }
        fputc('\n', stdout);
    }
E
Eric Blake 已提交
4474
    return true;
K
Karel Zak 已提交
4475 4476 4477 4478 4479 4480
}

/* ---------------
 * Utils for work with runtime commands data
 * ---------------
 */
4481 4482 4483
static void
vshCommandOptFree(vshCmdOpt * arg)
{
K
Karel Zak 已提交
4484 4485
    vshCmdOpt *a = arg;

4486
    while (a) {
K
Karel Zak 已提交
4487
        vshCmdOpt *tmp = a;
4488

K
Karel Zak 已提交
4489 4490
        a = a->next;

4491 4492
        VIR_FREE(tmp->data);
        VIR_FREE(tmp);
K
Karel Zak 已提交
4493 4494 4495 4496
    }
}

static void
4497
vshCommandFree(vshCmd *cmd)
4498
{
K
Karel Zak 已提交
4499 4500
    vshCmd *c = cmd;

4501
    while (c) {
K
Karel Zak 已提交
4502
        vshCmd *tmp = c;
4503

K
Karel Zak 已提交
4504 4505 4506 4507
        c = c->next;

        if (tmp->opts)
            vshCommandOptFree(tmp->opts);
4508
        VIR_FREE(tmp);
K
Karel Zak 已提交
4509 4510 4511
    }
}

E
Eric Blake 已提交
4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522
/**
 * vshCommandOpt:
 * @cmd: parsed command line to search
 * @name: option name to search for
 * @opt: result of the search
 *
 * 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 -2 if NAME is not valid
 * (-2 indicates a programming error).  No error messages are issued.
K
Karel Zak 已提交
4523
 */
E
Eric Blake 已提交
4524 4525
static int
vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
4526
{
E
Eric Blake 已提交
4527 4528
    vshCmdOpt *candidate = cmd->opts;
    const vshCmdOptDef *valid = cmd->def->opts;
4529

E
Eric Blake 已提交
4530 4531 4532 4533 4534 4535 4536
    /* See if option is present on command line.  */
    while (candidate) {
        if (STREQ(candidate->def->name, name)) {
            *opt = candidate;
            return 1;
        }
        candidate = candidate->next;
K
Karel Zak 已提交
4537
    }
E
Eric Blake 已提交
4538 4539 4540 4541 4542 4543 4544

    /* Option not present, see if command requires it.  */
    *opt = NULL;
    while (valid) {
        if (!valid->name)
            break;
        if (STREQ(name, valid->name))
E
Eric Blake 已提交
4545
            return (valid->flags & VSH_OFLAG_REQ) == 0 ? 0 : -1;
E
Eric Blake 已提交
4546 4547 4548 4549
        valid++;
    }
    /* If we got here, the name is unknown.  */
    return -2;
K
Karel Zak 已提交
4550 4551
}

E
Eric Blake 已提交
4552 4553
/**
 * vshCommandOptInt:
4554 4555 4556 4557 4558 4559 4560
 * @cmd command reference
 * @name option name
 * @value result
 *
 * Convert option to int
 * Return value:
 * >0 if option found and valid (@value updated)
E
Eric Blake 已提交
4561
 * 0 if option not found and not required (@value untouched)
4562
 * <0 in all other cases (@value untouched)
K
Karel Zak 已提交
4563 4564
 */
static int
4565
vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
4566
{
E
Eric Blake 已提交
4567 4568
    vshCmdOpt *arg;
    int ret;
4569

E
Eric Blake 已提交
4570 4571 4572 4573 4574 4575 4576
    ret = vshCommandOpt(cmd, name, &arg);
    if (ret <= 0)
        return ret;
    if (!arg->data) {
        /* only possible on bool, but if name is bool, this is a
         * programming bug */
        return -2;
4577
    }
E
Eric Blake 已提交
4578

E
Eric Blake 已提交
4579 4580 4581
    if (virStrToLong_i(arg->data, NULL, 10, value) < 0)
        return -1;
    return 1;
K
Karel Zak 已提交
4582 4583
}

4584

E
Eric Blake 已提交
4585 4586 4587 4588 4589 4590
/**
 * vshCommandOptUInt:
 * @cmd command reference
 * @name option name
 * @value result
 *
4591 4592 4593 4594 4595 4596
 * Convert option to unsigned int
 * See vshCommandOptInt()
 */
static int
vshCommandOptUInt(const vshCmd *cmd, const char *name, unsigned int *value)
{
E
Eric Blake 已提交
4597 4598
    vshCmdOpt *arg;
    int ret;
4599

E
Eric Blake 已提交
4600 4601 4602 4603 4604 4605 4606
    ret = vshCommandOpt(cmd, name, &arg);
    if (ret <= 0)
        return ret;
    if (!arg->data) {
        /* only possible on bool, but if name is bool, this is a
         * programming bug */
        return -2;
4607
    }
E
Eric Blake 已提交
4608

E
Eric Blake 已提交
4609 4610 4611
    if (virStrToLong_ui(arg->data, NULL, 10, value) < 0)
        return -1;
    return 1;
4612 4613 4614
}


4615
/*
E
Eric Blake 已提交
4616 4617 4618 4619 4620
 * vshCommandOptUL:
 * @cmd command reference
 * @name option name
 * @value result
 *
4621 4622 4623 4624 4625
 * Convert option to unsigned long
 * See vshCommandOptInt()
 */
static int
vshCommandOptUL(const vshCmd *cmd, const char *name, unsigned long *value)
4626
{
E
Eric Blake 已提交
4627 4628
    vshCmdOpt *arg;
    int ret;
4629

E
Eric Blake 已提交
4630 4631 4632 4633 4634 4635 4636
    ret = vshCommandOpt(cmd, name, &arg);
    if (ret <= 0)
        return ret;
    if (!arg->data) {
        /* only possible on bool, but if name is bool, this is a
         * programming bug */
        return -2;
4637
    }
E
Eric Blake 已提交
4638

E
Eric Blake 已提交
4639 4640 4641
    if (virStrToLong_ul(arg->data, NULL, 10, value) < 0)
        return -1;
    return 1;
4642 4643
}

E
Eric Blake 已提交
4644 4645 4646 4647 4648 4649
/**
 * vshCommandOptString:
 * @cmd command reference
 * @name option name
 * @value result
 *
K
Karel Zak 已提交
4650
 * Returns option as STRING
E
Eric Blake 已提交
4651 4652 4653 4654
 * 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)
K
Karel Zak 已提交
4655
 */
4656 4657
static int
vshCommandOptString(const vshCmd *cmd, const char *name, const char **value)
4658
{
E
Eric Blake 已提交
4659 4660 4661 4662 4663 4664 4665 4666 4667 4668
    vshCmdOpt *arg;
    int ret;

    ret = vshCommandOpt(cmd, name, &arg);
    if (ret <= 0)
        return ret;
    if (!arg->data) {
        /* only possible on bool, but if name is bool, this is a
         * programming bug */
        return -2;
4669
    }
4670

E
Eric Blake 已提交
4671
    if (!*arg->data && !(arg->def->flags & VSH_OFLAG_EMPTY_OK)) {
E
Eric Blake 已提交
4672 4673 4674 4675
        return -1;
    }
    *value = arg->data;
    return 1;
K
Karel Zak 已提交
4676 4677
}

E
Eric Blake 已提交
4678 4679 4680 4681 4682 4683
/**
 * vshCommandOptLongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
4684
 * Returns option as long long
4685
 * See vshCommandOptInt()
4686
 */
4687 4688 4689
static int
vshCommandOptLongLong(const vshCmd *cmd, const char *name,
                      long long *value)
4690
{
E
Eric Blake 已提交
4691 4692
    vshCmdOpt *arg;
    int ret;
4693

E
Eric Blake 已提交
4694 4695 4696 4697 4698 4699 4700
    ret = vshCommandOpt(cmd, name, &arg);
    if (ret <= 0)
        return ret;
    if (!arg->data) {
        /* only possible on bool, but if name is bool, this is a
         * programming bug */
        return -2;
4701
    }
E
Eric Blake 已提交
4702

E
Eric Blake 已提交
4703 4704 4705
    if (virStrToLong_ll(arg->data, NULL, 10, value) < 0)
        return -1;
    return 1;
4706 4707
}

E
Eric Blake 已提交
4708 4709 4710 4711 4712 4713 4714 4715 4716
/**
 * vshCommandOptULongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
 * Returns option as long long
 * See vshCommandOptInt()
 */
4717 4718 4719 4720
static int
vshCommandOptULongLong(const vshCmd *cmd, const char *name,
                       unsigned long long *value)
{
E
Eric Blake 已提交
4721 4722
    vshCmdOpt *arg;
    int ret;
4723

E
Eric Blake 已提交
4724 4725 4726 4727 4728 4729 4730
    ret = vshCommandOpt(cmd, name, &arg);
    if (ret <= 0)
        return ret;
    if (!arg->data) {
        /* only possible on bool, but if name is bool, this is a
         * programming bug */
        return -2;
4731
    }
E
Eric Blake 已提交
4732

E
Eric Blake 已提交
4733 4734 4735
    if (virStrToLong_ull(arg->data, NULL, 10, value) < 0)
        return -1;
    return 1;
4736 4737 4738
}


E
Eric Blake 已提交
4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768
/**
 * vshCommandOptScaledInt:
 * @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()
 */
static int
vshCommandOptScaledInt(const vshCmd *cmd, const char *name,
                       unsigned long long *value, int scale,
                       unsigned long long max)
{
    const char *str;
    int ret;
    char *end;

    ret = vshCommandOptString(cmd, name, &str);
    if (ret <= 0)
        return ret;
    if (virStrToLong_ull(str, &end, 10, value) < 0 ||
        virScaleInteger(value, end, scale, max) < 0)
        return -1;
    return 1;
}


E
Eric Blake 已提交
4769 4770 4771 4772 4773 4774 4775 4776 4777
/**
 * 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.
K
Karel Zak 已提交
4778
 */
E
Eric Blake 已提交
4779
static bool
4780
vshCommandOptBool(const vshCmd *cmd, const char *name)
4781
{
E
Eric Blake 已提交
4782 4783 4784
    vshCmdOpt *dummy;

    return vshCommandOpt(cmd, name, &dummy) == 1;
K
Karel Zak 已提交
4785 4786
}

E
Eric Blake 已提交
4787 4788 4789 4790 4791
/**
 * vshCommandOptArgv:
 * @cmd command reference
 * @opt starting point for the search
 *
4792 4793
 * Returns the next argv argument after OPT (or the first one if OPT
 * is NULL), or NULL if no more are present.
4794
 *
4795
 * Requires that a VSH_OT_ARGV option be last in the
4796 4797
 * list of supported options in CMD->def->opts.
 */
4798 4799
static const vshCmdOpt *
vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt)
4800
{
4801
    opt = opt ? opt->next : cmd->opts;
4802 4803

    while (opt) {
E
Eric Blake 已提交
4804
        if (opt->def->type == VSH_OT_ARGV) {
4805
            return opt;
4806 4807 4808 4809 4810 4811
        }
        opt = opt->next;
    }
    return NULL;
}

J
Jim Meyering 已提交
4812 4813 4814 4815
/* Determine whether CMD->opts includes an option with name OPTNAME.
   If not, give a diagnostic and return false.
   If so, return true.  */
static bool
4816
cmd_has_option(vshControl *ctl, const vshCmd *cmd, const char *optname)
J
Jim Meyering 已提交
4817 4818 4819 4820 4821 4822
{
    /* Iterate through cmd->opts, to ensure that there is an entry
       with name OPTNAME and type VSH_OT_DATA. */
    bool found = false;
    const vshCmdOpt *opt;
    for (opt = cmd->opts; opt; opt = opt->next) {
4823
        if (STREQ(opt->def->name, optname) && opt->def->type == VSH_OT_DATA) {
J
Jim Meyering 已提交
4824 4825 4826 4827 4828 4829
            found = true;
            break;
        }
    }

    if (!found)
4830
        vshError(ctl, _("internal error: virsh %s: no %s VSH_OT_DATA option"),
J
Jim Meyering 已提交
4831 4832 4833
                 cmd->def->name, optname);
    return found;
}
4834

K
Karel Zak 已提交
4835
static virDomainPtr
J
Jim Meyering 已提交
4836
vshCommandOptDomainBy(vshControl *ctl, const vshCmd *cmd,
4837
                      const char **name, int flag)
4838
{
K
Karel Zak 已提交
4839
    virDomainPtr dom = NULL;
4840
    const char *n = NULL;
K
Karel Zak 已提交
4841
    int id;
J
Jim Meyering 已提交
4842
    const char *optname = "domain";
4843
    if (!cmd_has_option(ctl, cmd, optname))
J
Jim Meyering 已提交
4844
        return NULL;
4845

4846
    if (vshCommandOptString(cmd, optname, &n) <= 0)
4847 4848
        return NULL;

4849
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
4850 4851
             cmd->def->name, optname, n);

K
Karel Zak 已提交
4852 4853
    if (name)
        *name = n;
4854

K
Karel Zak 已提交
4855
    /* try it by ID */
4856
    if (flag & VSH_BYID) {
4857
        if (virStrToLong_i(n, NULL, 10, &id) == 0 && id >= 0) {
4858 4859
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: <%s> seems like domain ID\n",
K
Karel Zak 已提交
4860 4861 4862
                     cmd->def->name, optname);
            dom = virDomainLookupByID(ctl->conn, id);
        }
4863
    }
K
Karel Zak 已提交
4864
    /* try it by UUID */
4865
    if (dom==NULL && (flag & VSH_BYUUID) && strlen(n)==VIR_UUID_STRING_BUFLEN-1) {
4866
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain UUID\n",
4867
                 cmd->def->name, optname);
K
Karel Zak 已提交
4868
        dom = virDomainLookupByUUIDString(ctl->conn, n);
K
Karel Zak 已提交
4869
    }
K
Karel Zak 已提交
4870
    /* try it by NAME */
4871
    if (dom==NULL && (flag & VSH_BYNAME)) {
4872
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain NAME\n",
4873
                 cmd->def->name, optname);
K
Karel Zak 已提交
4874
        dom = virDomainLookupByName(ctl->conn, n);
4875
    }
K
Karel Zak 已提交
4876

4877
    if (!dom)
4878
        vshError(ctl, _("failed to get domain '%s'"), n);
4879

K
Karel Zak 已提交
4880 4881 4882
    return dom;
}

4883
static virSecretPtr
4884
vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, const char **name)
4885 4886
{
    virSecretPtr secret = NULL;
4887
    const char *n = NULL;
4888 4889
    const char *optname = "secret";

4890
    if (!cmd_has_option(ctl, cmd, optname))
4891 4892
        return NULL;

4893
    if (vshCommandOptString(cmd, optname, &n) <= 0)
4894 4895
        return NULL;

4896 4897
    vshDebug(ctl, VSH_ERR_DEBUG,
             "%s: found option <%s>: %s\n", cmd->def->name, optname, n);
4898 4899 4900 4901 4902 4903 4904

    if (name != NULL)
        *name = n;

    secret = virSecretLookupByUUIDString(ctl->conn, n);

    if (secret == NULL)
4905
        vshError(ctl, _("failed to get secret '%s'"), n);
4906 4907 4908 4909

    return secret;
}

K
Karel Zak 已提交
4910 4911 4912
/*
 * Executes command(s) and returns return code from last command
 */
E
Eric Blake 已提交
4913
static bool
4914
vshCommandRun(vshControl *ctl, const vshCmd *cmd)
4915
{
E
Eric Blake 已提交
4916
    bool ret = true;
4917 4918

    while (cmd) {
K
Karel Zak 已提交
4919
        struct timeval before, after;
4920
        bool enable_timing = ctl->timing;
4921

4922 4923
        if ((ctl->conn == NULL || disconnected) &&
            !(cmd->def->flags & VSH_CMD_FLAG_NOCONNECT))
4924 4925
            vshReconnect(ctl);

4926
        if (enable_timing)
K
Karel Zak 已提交
4927
            GETTIMEOFDAY(&before);
4928

K
Karel Zak 已提交
4929 4930
        ret = cmd->def->handler(ctl, cmd);

4931
        if (enable_timing)
K
Karel Zak 已提交
4932
            GETTIMEOFDAY(&after);
4933

4934 4935 4936 4937 4938 4939 4940 4941 4942 4943
        /* 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++;

4944
        if (!ret)
J
John Levon 已提交
4945 4946
            virshReportError(ctl);

4947
        if (!ret && disconnected != 0)
4948 4949
            vshReconnect(ctl);

4950
        if (STREQ(cmd->def->name, "quit"))        /* hack ... */
K
Karel Zak 已提交
4951 4952
            return ret;

4953
        if (enable_timing)
4954
            vshPrint(ctl, _("\n(Time: %.3f ms)\n\n"),
4955 4956
                     DIFF_MSEC(&after, &before));
        else
K
Karel Zak 已提交
4957
            vshPrintExtra(ctl, "\n");
K
Karel Zak 已提交
4958 4959 4960 4961 4962 4963
        cmd = cmd->next;
    }
    return ret;
}

/* ---------------
4964
 * Command parsing
K
Karel Zak 已提交
4965 4966 4967
 * ---------------
 */

4968 4969 4970 4971 4972 4973 4974 4975
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 {
4976
    vshCommandToken(*getNextArg)(vshControl *, struct __vshCommandParser *,
4977
                                  char **);
L
Lai Jiangshan 已提交
4978
    /* vshCommandStringGetArg() */
4979
    char *pos;
L
Lai Jiangshan 已提交
4980 4981 4982
    /* vshCommandArgvGetArg() */
    char **arg_pos;
    char **arg_end;
4983 4984
} vshCommandParser;

E
Eric Blake 已提交
4985
static bool
4986
vshCommandParse(vshControl *ctl, vshCommandParser *parser)
4987
{
K
Karel Zak 已提交
4988 4989 4990
    char *tkdata = NULL;
    vshCmd *clast = NULL;
    vshCmdOpt *first = NULL;
4991

K
Karel Zak 已提交
4992 4993 4994 4995
    if (ctl->cmd) {
        vshCommandFree(ctl->cmd);
        ctl->cmd = NULL;
    }
4996

4997
    while (1) {
K
Karel Zak 已提交
4998
        vshCmdOpt *last = NULL;
4999
        const vshCmdDef *cmd = NULL;
5000
        vshCommandToken tk;
L
Lai Jiangshan 已提交
5001
        bool data_only = false;
5002 5003 5004
        uint32_t opts_need_arg = 0;
        uint32_t opts_required = 0;
        uint32_t opts_seen = 0;
5005

K
Karel Zak 已提交
5006
        first = NULL;
5007

5008
        while (1) {
5009
            const vshCmdOptDef *opt = NULL;
5010

K
Karel Zak 已提交
5011
            tkdata = NULL;
5012
            tk = parser->getNextArg(ctl, parser, &tkdata);
5013 5014

            if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
5015
                goto syntaxError;
H
Hu Tao 已提交
5016 5017
            if (tk != VSH_TK_ARG) {
                VIR_FREE(tkdata);
5018
                break;
H
Hu Tao 已提交
5019
            }
5020 5021

            if (cmd == NULL) {
K
Karel Zak 已提交
5022 5023
                /* first token must be command name */
                if (!(cmd = vshCmddefSearch(tkdata))) {
5024
                    vshError(ctl, _("unknown command: '%s'"), tkdata);
5025
                    goto syntaxError;   /* ... or ignore this command only? */
K
Karel Zak 已提交
5026
                }
5027 5028 5029 5030 5031 5032 5033
                if (vshCmddefOptParse(cmd, &opts_need_arg,
                                      &opts_required) < 0) {
                    vshError(ctl,
                             _("internal error: bad options in command: '%s'"),
                             tkdata);
                    goto syntaxError;
                }
5034
                VIR_FREE(tkdata);
L
Lai Jiangshan 已提交
5035 5036 5037 5038
            } else if (data_only) {
                goto get_data;
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       c_isalnum(tkdata[2])) {
5039
                char *optstr = strchr(tkdata + 2, '=');
5040 5041
                int opt_index;

5042 5043 5044 5045
                if (optstr) {
                    *optstr = '\0'; /* convert the '=' to '\0' */
                    optstr = vshStrdup(ctl, optstr + 1);
                }
5046
                if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2,
5047
                                               &opts_seen, &opt_index))) {
5048
                    VIR_FREE(optstr);
K
Karel Zak 已提交
5049 5050
                    goto syntaxError;
                }
5051
                VIR_FREE(tkdata);
K
Karel Zak 已提交
5052 5053 5054

                if (opt->type != VSH_OT_BOOL) {
                    /* option data */
5055 5056 5057
                    if (optstr)
                        tkdata = optstr;
                    else
5058
                        tk = parser->getNextArg(ctl, parser, &tkdata);
5059
                    if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
5060
                        goto syntaxError;
5061
                    if (tk != VSH_TK_ARG) {
5062
                        vshError(ctl,
5063
                                 _("expected syntax: --%s <%s>"),
5064 5065
                                 opt->name,
                                 opt->type ==
5066
                                 VSH_OT_INT ? _("number") : _("string"));
K
Karel Zak 已提交
5067 5068
                        goto syntaxError;
                    }
5069 5070
                    if (opt->type != VSH_OT_ARGV)
                        opts_need_arg &= ~(1 << opt_index);
5071 5072 5073 5074 5075 5076 5077 5078
                } else {
                    tkdata = NULL;
                    if (optstr) {
                        vshError(ctl, _("invalid '=' after option --%s"),
                                opt->name);
                        VIR_FREE(optstr);
                        goto syntaxError;
                    }
K
Karel Zak 已提交
5079
                }
L
Lai Jiangshan 已提交
5080 5081 5082 5083
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       tkdata[2] == '\0') {
                data_only = true;
                continue;
5084
            } else {
L
Lai Jiangshan 已提交
5085
get_data:
5086 5087
                if (!(opt = vshCmddefGetData(cmd, &opts_need_arg,
                                             &opts_seen))) {
5088
                    vshError(ctl, _("unexpected data '%s'"), tkdata);
K
Karel Zak 已提交
5089 5090 5091 5092 5093
                    goto syntaxError;
                }
            }
            if (opt) {
                /* save option */
5094
                vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt));
5095

K
Karel Zak 已提交
5096 5097 5098 5099
                arg->def = opt;
                arg->data = tkdata;
                arg->next = NULL;
                tkdata = NULL;
5100

K
Karel Zak 已提交
5101 5102 5103 5104 5105
                if (!first)
                    first = arg;
                if (last)
                    last->next = arg;
                last = arg;
5106

5107
                vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n",
5108 5109
                         cmd->name,
                         opt->name,
5110 5111
                         opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"),
                         opt->type != VSH_OT_BOOL ? arg->data : _("(none)"));
K
Karel Zak 已提交
5112 5113
            }
        }
5114

D
Daniel Veillard 已提交
5115
        /* command parsed -- allocate new struct for the command */
K
Karel Zak 已提交
5116
        if (cmd) {
5117
            vshCmd *c = vshMalloc(ctl, sizeof(vshCmd));
5118

K
Karel Zak 已提交
5119 5120 5121 5122
            c->opts = first;
            c->def = cmd;
            c->next = NULL;

5123
            if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) {
5124
                VIR_FREE(c);
5125
                goto syntaxError;
5126
            }
5127

K
Karel Zak 已提交
5128 5129 5130 5131 5132 5133
            if (!ctl->cmd)
                ctl->cmd = c;
            if (clast)
                clast->next = c;
            clast = c;
        }
5134 5135 5136

        if (tk == VSH_TK_END)
            break;
K
Karel Zak 已提交
5137
    }
5138

E
Eric Blake 已提交
5139
    return true;
K
Karel Zak 已提交
5140

5141
 syntaxError:
5142
    if (ctl->cmd) {
K
Karel Zak 已提交
5143
        vshCommandFree(ctl->cmd);
5144 5145
        ctl->cmd = NULL;
    }
K
Karel Zak 已提交
5146 5147
    if (first)
        vshCommandOptFree(first);
5148
    VIR_FREE(tkdata);
E
Eric Blake 已提交
5149
    return false;
K
Karel Zak 已提交
5150 5151
}

5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169
/* --------------------
 * 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;
}

E
Eric Blake 已提交
5170 5171
static bool
vshCommandArgvParse(vshControl *ctl, int nargs, char **argv)
5172 5173 5174 5175
{
    vshCommandParser parser;

    if (nargs <= 0)
E
Eric Blake 已提交
5176
        return false;
5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248

    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;
}

E
Eric Blake 已提交
5249 5250
static bool
vshCommandStringParse(vshControl *ctl, char *cmdstr)
5251 5252 5253 5254
{
    vshCommandParser parser;

    if (cmdstr == NULL || *cmdstr == '\0')
E
Eric Blake 已提交
5255
        return false;
5256 5257 5258 5259 5260 5261

    parser.pos = cmdstr;
    parser.getNextArg = vshCommandStringGetArg;
    return vshCommandParse(ctl, &parser);
}

K
Karel Zak 已提交
5262
/* ---------------
5263
 * Misc utils
K
Karel Zak 已提交
5264 5265
 * ---------------
 */
5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293
static 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;
}

5294 5295
/* Return a non-NULL string representation of a typed parameter; exit
 * if we are out of memory.  */
5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326
static 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:
        ret = virAsprintf(&str, "%s", item->value.b ? _("yes") : _("no"));
        break;

5327 5328 5329 5330
    case VIR_TYPED_PARAM_STRING:
        str = vshStrdup(ctl, item->value.s);
        break;

5331
    default:
5332
        vshError(ctl, _("unimplemented parameter type %d"), item->type);
5333 5334
    }

5335
    if (ret < 0) {
5336
        vshError(ctl, "%s", _("Out of memory"));
5337 5338
        exit(EXIT_FAILURE);
    }
5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361
    return str;
}

static virTypedParameterPtr
vshFindTypedParamByName(const char *name, virTypedParameterPtr list, int count)
{
    int i = count;
    virTypedParameterPtr found = list;

    if (!list || !name)
        return NULL;

    while (i-- > 0) {
        if (STREQ(name, found->field))
            return found;

        found++; /* go to next struct in array */
    }

    /* not found */
    return NULL;
}

E
Eric Blake 已提交
5362
static bool
5363
vshConnectionUsability(vshControl *ctl, virConnectPtr conn)
5364
{
5365 5366
    /* TODO: use something like virConnectionState() to
     *       check usability of the connection
K
Karel Zak 已提交
5367 5368
     */
    if (!conn) {
5369
        vshError(ctl, "%s", _("no valid connection"));
E
Eric Blake 已提交
5370
        return false;
K
Karel Zak 已提交
5371
    }
E
Eric Blake 已提交
5372
    return true;
K
Karel Zak 已提交
5373 5374
}

K
Karel Zak 已提交
5375
static void
5376
vshDebug(vshControl *ctl, int level, const char *format, ...)
5377
{
K
Karel Zak 已提交
5378
    va_list ap;
5379
    char *str;
K
Karel Zak 已提交
5380

5381 5382 5383 5384 5385 5386 5387
    /* Aligning log levels to that of libvirt.
     * Traces with levels >=  user-specified-level
     * gets logged into file
     */
    if (level < ctl->debug)
        return;

5388
    va_start(ap, format);
5389
    vshOutputLogFile(ctl, level, format, ap);
5390 5391
    va_end(ap);

K
Karel Zak 已提交
5392
    va_start(ap, format);
5393 5394 5395 5396 5397
    if (virVasprintf(&str, format, ap) < 0) {
        /* Skip debug messages on low memory */
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
5398
    va_end(ap);
5399 5400
    fputs(str, stdout);
    VIR_FREE(str);
K
Karel Zak 已提交
5401 5402 5403
}

static void
5404
vshPrintExtra(vshControl *ctl, const char *format, ...)
5405
{
K
Karel Zak 已提交
5406
    va_list ap;
5407
    char *str;
5408

5409
    if (ctl && ctl->quiet)
K
Karel Zak 已提交
5410
        return;
5411

K
Karel Zak 已提交
5412
    va_start(ap, format);
5413 5414 5415 5416 5417
    if (virVasprintf(&str, format, ap) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
5418
    va_end(ap);
5419
    fputs(str, stdout);
5420
    VIR_FREE(str);
K
Karel Zak 已提交
5421 5422
}

K
Karel Zak 已提交
5423

K
Karel Zak 已提交
5424
static void
5425
vshError(vshControl *ctl, const char *format, ...)
5426
{
K
Karel Zak 已提交
5427
    va_list ap;
5428
    char *str;
5429

5430 5431 5432 5433 5434
    if (ctl != NULL) {
        va_start(ap, format);
        vshOutputLogFile(ctl, VSH_ERR_ERROR, format, ap);
        va_end(ap);
    }
5435

5436 5437 5438 5439
    /* 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);
5440
    fputs(_("error: "), stderr);
5441

K
Karel Zak 已提交
5442
    va_start(ap, format);
5443 5444 5445
    /* We can't recursively call vshError on an OOM situation, so ignore
       failure here. */
    ignore_value(virVasprintf(&str, format, ap));
K
Karel Zak 已提交
5446 5447
    va_end(ap);

5448
    fprintf(stderr, "%s\n", NULLSTR(str));
5449
    fflush(stderr);
5450
    VIR_FREE(str);
K
Karel Zak 已提交
5451 5452
}

5453

J
Jiri Denemark 已提交
5454 5455 5456 5457 5458
static void
vshEventLoop(void *opaque)
{
    vshControl *ctl = opaque;

5459 5460 5461 5462 5463 5464 5465 5466 5467 5468
    while (1) {
        bool quit;
        virMutexLock(&ctl->lock);
        quit = ctl->quit;
        virMutexUnlock(&ctl->lock);

        if (quit)
            break;

        if (virEventRunDefaultImpl() < 0)
J
Jiri Denemark 已提交
5469 5470 5471 5472 5473
            virshReportError(ctl);
    }
}


K
Karel Zak 已提交
5474
/*
5475
 * Initialize connection.
K
Karel Zak 已提交
5476
 */
E
Eric Blake 已提交
5477
static bool
5478
vshInit(vshControl *ctl)
5479
{
5480 5481
    char *debugEnv;

K
Karel Zak 已提交
5482
    if (ctl->conn)
E
Eric Blake 已提交
5483
        return false;
K
Karel Zak 已提交
5484

J
Jiri Denemark 已提交
5485
    if (ctl->debug == VSH_DEBUG_DEFAULT) {
5486 5487 5488
        /* log level not set from commandline, check env variable */
        debugEnv = getenv("VIRSH_DEBUG");
        if (debugEnv) {
J
Jiri Denemark 已提交
5489 5490 5491
            int debug;
            if (virStrToLong_i(debugEnv, NULL, 10, &debug) < 0 ||
                debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) {
5492 5493
                vshError(ctl, "%s",
                         _("VIRSH_DEBUG not set with a valid numeric value"));
J
Jiri Denemark 已提交
5494 5495
            } else {
                ctl->debug = debug;
5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507
            }
        }
    }

    if (ctl->logfile == NULL) {
        /* log file not set from cmdline */
        debugEnv = getenv("VIRSH_LOG_FILE");
        if (debugEnv && *debugEnv) {
            ctl->logfile = vshStrdup(ctl, debugEnv);
        }
    }

5508 5509
    vshOpenLogFile(ctl);

5510 5511
    /* set up the library error handler */
    virSetErrorFunc(NULL, virshErrorHandler);
5512

5513 5514 5515
    /* set up the signals handlers to catch disconnections */
    vshSetupSignals();

5516
    if (virEventRegisterDefaultImpl() < 0)
E
Eric Blake 已提交
5517
        return false;
5518

J
Jiri Denemark 已提交
5519 5520 5521 5522
    if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0)
        return false;
    ctl->eventLoopStarted = true;

5523 5524 5525 5526
    if (ctl->name) {
        ctl->conn = virConnectOpenAuth(ctl->name,
                                       virConnectAuthPtrDefault,
                                       ctl->readonly ? VIR_CONNECT_RO : 0);
5527

5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538
        /* 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) {
            virshReportError(ctl);
            vshError(ctl, "%s", _("failed to connect to the hypervisor"));
            return false;
        }
5539
    }
K
Karel Zak 已提交
5540

E
Eric Blake 已提交
5541
    return true;
K
Karel Zak 已提交
5542 5543
}

5544 5545
#define LOGFILE_FLAGS (O_WRONLY | O_APPEND | O_CREAT | O_SYNC)

5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564
/**
 * vshOpenLogFile:
 *
 * Open log file.
 */
static void
vshOpenLogFile(vshControl *ctl)
{
    struct stat st;

    if (ctl->logfile == NULL)
        return;

    /* check log file */
    if (stat(ctl->logfile, &st) == -1) {
        switch (errno) {
            case ENOENT:
                break;
            default:
5565
                vshError(ctl, "%s",
J
Jim Meyering 已提交
5566
                         _("failed to get the log file information"));
5567
                exit(EXIT_FAILURE);
5568 5569 5570
        }
    } else {
        if (!S_ISREG(st.st_mode)) {
5571 5572
            vshError(ctl, "%s", _("the log path is not a file"));
            exit(EXIT_FAILURE);
5573 5574 5575 5576
        }
    }

    /* log file open */
5577
    if ((ctl->log_fd = open(ctl->logfile, LOGFILE_FLAGS, FILE_MODE)) < 0) {
5578
        vshError(ctl, "%s",
J
Jim Meyering 已提交
5579
                 _("failed to open the log file. check the log file path"));
5580
        exit(EXIT_FAILURE);
5581 5582 5583 5584 5585 5586 5587 5588 5589
    }
}

/**
 * vshOutputLogFile:
 *
 * Outputting an error to log file.
 */
static void
5590 5591
vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format,
                 va_list ap)
5592
{
5593 5594 5595
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *str;
    size_t len;
5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609
    const char *lvl = "";
    struct timeval stTimeval;
    struct tm *stTm;

    if (ctl->log_fd == -1)
        return;

    /**
     * create log format
     *
     * [YYYY.MM.DD HH:MM:SS SIGNATURE PID] LOG_LEVEL message
    */
    gettimeofday(&stTimeval, NULL);
    stTm = localtime(&stTimeval.tv_sec);
5610
    virBufferAsprintf(&buf, "[%d.%02d.%02d %02d:%02d:%02d %s %d] ",
5611 5612 5613 5614 5615 5616
                      (1900 + stTm->tm_year),
                      (1 + stTm->tm_mon),
                      stTm->tm_mday,
                      stTm->tm_hour,
                      stTm->tm_min,
                      stTm->tm_sec,
5617 5618
                      SIGN_NAME,
                      (int) getpid());
5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638
    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;
    }
5639 5640 5641
    virBufferAsprintf(&buf, "%s ", lvl);
    virBufferVasprintf(&buf, msg_format, ap);
    virBufferAddChar(&buf, '\n');
5642

5643 5644
    if (virBufferError(&buf))
        goto error;
5645

5646 5647 5648 5649 5650
    str = virBufferContentAndReset(&buf);
    len = strlen(str);
    if (len > 1 && str[len - 2] == '\n') {
        str[len - 1] = '\0';
        len--;
5651
    }
5652

5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663
    /* write log */
    if (safewrite(ctl->log_fd, str, len) < 0)
        goto error;

    return;

error:
    vshCloseLogFile(ctl);
    vshError(ctl, "%s", _("failed to write the log file"));
    virBufferFreeAndReset(&buf);
    VIR_FREE(str);
5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674
}

/**
 * vshCloseLogFile:
 *
 * Close log file.
 */
static void
vshCloseLogFile(vshControl *ctl)
{
    /* log file close */
5675 5676 5677
    if (VIR_CLOSE(ctl->log_fd) < 0) {
        vshError(ctl, _("%s: failed to write log file: %s"),
                 ctl->logfile ? ctl->logfile : "?", strerror (errno));
5678 5679 5680
    }

    if (ctl->logfile) {
5681
        VIR_FREE(ctl->logfile);
5682 5683 5684 5685
        ctl->logfile = NULL;
    }
}

5686
#ifdef USE_READLINE
5687

K
Karel Zak 已提交
5688 5689 5690 5691 5692
/* -----------------
 * Readline stuff
 * -----------------
 */

5693
/*
K
Karel Zak 已提交
5694 5695
 * Generator function for command completion.  STATE lets us
 * know whether to start from scratch; without any state
5696
 * (i.e. STATE == 0), then we start at the top of the list.
K
Karel Zak 已提交
5697 5698
 */
static char *
5699 5700
vshReadlineCommandGenerator(const char *text, int state)
{
5701
    static int grp_list_index, cmd_list_index, len;
K
Karel Zak 已提交
5702
    const char *name;
5703 5704
    const vshCmdGrp *grp;
    const vshCmdDef *cmds;
K
Karel Zak 已提交
5705 5706

    if (!state) {
5707 5708
        grp_list_index = 0;
        cmd_list_index = 0;
5709
        len = strlen(text);
K
Karel Zak 已提交
5710 5711
    }

5712 5713
    grp = cmdGroups;

K
Karel Zak 已提交
5714
    /* Return the next name which partially matches from the
5715
     * command list.
K
Karel Zak 已提交
5716
     */
5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730
    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++;
        }
K
Karel Zak 已提交
5731 5732 5733 5734 5735 5736 5737
    }

    /* If no names matched, then return NULL. */
    return NULL;
}

static char *
5738 5739
vshReadlineOptionsGenerator(const char *text, int state)
{
K
Karel Zak 已提交
5740
    static int list_index, len;
5741
    static const vshCmdDef *cmd = NULL;
K
Karel Zak 已提交
5742
    const char *name;
K
Karel Zak 已提交
5743 5744 5745 5746 5747 5748 5749 5750 5751

    if (!state) {
        /* determine command name */
        char *p;
        char *cmdname;

        if (!(p = strchr(rl_line_buffer, ' ')))
            return NULL;

5752
        cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1);
5753
        memcpy(cmdname, rl_line_buffer, p - rl_line_buffer);
K
Karel Zak 已提交
5754 5755 5756

        cmd = vshCmddefSearch(cmdname);
        list_index = 0;
5757
        len = strlen(text);
5758
        VIR_FREE(cmdname);
K
Karel Zak 已提交
5759 5760 5761 5762
    }

    if (!cmd)
        return NULL;
5763

5764 5765 5766
    if (!cmd->opts)
        return NULL;

K
Karel Zak 已提交
5767
    while ((name = cmd->opts[list_index].name)) {
5768
        const vshCmdOptDef *opt = &cmd->opts[list_index];
K
Karel Zak 已提交
5769
        char *res;
5770

K
Karel Zak 已提交
5771
        list_index++;
5772

5773
        if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV)
K
Karel Zak 已提交
5774 5775
            /* ignore non --option */
            continue;
5776

K
Karel Zak 已提交
5777
        if (len > 2) {
5778
            if (STRNEQLEN(name, text + 2, len - 2))
K
Karel Zak 已提交
5779 5780
                continue;
        }
5781
        res = vshMalloc(NULL, strlen(name) + 3);
5782
        snprintf(res, strlen(name) + 3,  "--%s", name);
K
Karel Zak 已提交
5783 5784 5785 5786 5787 5788 5789 5790
        return res;
    }

    /* If no names matched, then return NULL. */
    return NULL;
}

static char **
5791 5792 5793
vshReadlineCompletion(const char *text, int start,
                      int end ATTRIBUTE_UNUSED)
{
K
Karel Zak 已提交
5794 5795
    char **matches = (char **) NULL;

5796
    if (start == 0)
K
Karel Zak 已提交
5797
        /* command name generator */
5798
        matches = rl_completion_matches(text, vshReadlineCommandGenerator);
K
Karel Zak 已提交
5799 5800
    else
        /* commands options */
5801
        matches = rl_completion_matches(text, vshReadlineOptionsGenerator);
K
Karel Zak 已提交
5802 5803 5804 5805
    return matches;
}


5806 5807
static int
vshReadlineInit(vshControl *ctl)
5808
{
5809 5810
    char *userdir = NULL;

K
Karel Zak 已提交
5811 5812 5813 5814 5815
    /* 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;
5816 5817 5818

    /* Limit the total size of the history buffer */
    stifle_history(500);
5819

5820
    /* Prepare to read/write history from/to the $XDG_CACHE_HOME/virsh/history file */
5821
    userdir = virGetUserCacheDirectory();
5822

5823 5824
    if (userdir == NULL) {
        vshError(ctl, "%s", _("Could not determine home directory"));
5825
        return -1;
5826
    }
5827

5828
    if (virAsprintf(&ctl->historydir, "%s/virsh", userdir) < 0) {
5829
        vshError(ctl, "%s", _("Out of memory"));
5830
        VIR_FREE(userdir);
5831 5832 5833 5834 5835
        return -1;
    }

    if (virAsprintf(&ctl->historyfile, "%s/history", ctl->historydir) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
5836
        VIR_FREE(userdir);
5837 5838 5839
        return -1;
    }

5840
    VIR_FREE(userdir);
5841 5842 5843 5844 5845 5846 5847

    read_history(ctl->historyfile);

    return 0;
}

static void
5848
vshReadlineDeinit(vshControl *ctl)
5849 5850
{
    if (ctl->historyfile != NULL) {
5851 5852
        if (virFileMakePathWithMode(ctl->historydir, 0755) < 0 &&
            errno != EEXIST) {
5853 5854
            char ebuf[1024];
            vshError(ctl, _("Failed to create '%s': %s"),
5855
                     ctl->historydir, virStrerror(errno, ebuf, sizeof(ebuf)));
E
Eric Blake 已提交
5856
        } else {
5857
            write_history(ctl->historyfile);
E
Eric Blake 已提交
5858
        }
5859 5860
    }

5861 5862
    VIR_FREE(ctl->historydir);
    VIR_FREE(ctl->historyfile);
K
Karel Zak 已提交
5863 5864
}

5865
static char *
5866
vshReadline(vshControl *ctl ATTRIBUTE_UNUSED, const char *prompt)
5867
{
5868
    return readline(prompt);
5869 5870
}

5871
#else /* !USE_READLINE */
5872

5873
static int
5874
vshReadlineInit(vshControl *ctl ATTRIBUTE_UNUSED)
5875 5876 5877 5878 5879
{
    /* empty */
    return 0;
}

5880
static void
5881
vshReadlineDeinit(vshControl *ctl ATTRIBUTE_UNUSED)
5882 5883 5884 5885 5886
{
    /* empty */
}

static char *
5887
vshReadline(vshControl *ctl, const char *prompt)
5888 5889 5890 5891 5892
{
    char line[1024];
    char *r;
    int len;

5893 5894
    fputs(prompt, stdout);
    r = fgets(line, sizeof(line), stdin);
5895 5896 5897
    if (r == NULL) return NULL; /* EOF */

    /* Chomp trailing \n */
5898
    len = strlen(r);
5899 5900 5901
    if (len > 0 && r[len-1] == '\n')
        r[len-1] = '\0';

5902
    return vshStrdup(ctl, r);
5903 5904
}

5905
#endif /* !USE_READLINE */
5906

5907 5908 5909 5910 5911 5912
static void
vshDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED)
{
    /* nothing to be done here */
}

K
Karel Zak 已提交
5913
/*
J
Jim Meyering 已提交
5914
 * Deinitialize virsh
K
Karel Zak 已提交
5915
 */
E
Eric Blake 已提交
5916
static bool
5917
vshDeinit(vshControl *ctl)
5918
{
5919
    vshReadlineDeinit(ctl);
5920
    vshCloseLogFile(ctl);
5921
    VIR_FREE(ctl->name);
K
Karel Zak 已提交
5922
    if (ctl->conn) {
5923 5924 5925
        int ret;
        if ((ret = virConnectClose(ctl->conn)) != 0) {
            vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
K
Karel Zak 已提交
5926 5927
        }
    }
D
Daniel P. Berrange 已提交
5928 5929
    virResetLastError();

J
Jiri Denemark 已提交
5930
    if (ctl->eventLoopStarted) {
5931 5932 5933 5934
        int timer;

        virMutexLock(&ctl->lock);
        ctl->quit = true;
J
Jiri Denemark 已提交
5935
        /* HACK: Add a dummy timeout to break event loop */
5936 5937 5938 5939 5940
        timer = virEventAddTimeout(0, vshDeinitTimer, NULL, NULL);
        virMutexUnlock(&ctl->lock);

        virThreadJoin(&ctl->eventLoop);

J
Jiri Denemark 已提交
5941 5942 5943 5944 5945 5946
        if (timer != -1)
            virEventRemoveTimeout(timer);

        ctl->eventLoopStarted = false;
    }

5947 5948
    virMutexDestroy(&ctl->lock);

E
Eric Blake 已提交
5949
    return true;
K
Karel Zak 已提交
5950
}
5951

K
Karel Zak 已提交
5952 5953 5954 5955
/*
 * Print usage
 */
static void
5956
vshUsage(void)
5957
{
5958
    const vshCmdGrp *grp;
5959
    const vshCmdDef *cmd;
5960

L
Lai Jiangshan 已提交
5961 5962
    fprintf(stdout, _("\n%s [options]... [<command_string>]"
                      "\n%s [options]... <command> [args...]\n\n"
5963
                      "  options:\n"
5964
                      "    -c | --connect=URI      hypervisor connection URI\n"
5965
                      "    -r | --readonly         connect readonly\n"
5966
                      "    -d | --debug=NUM        debug level [0-4]\n"
5967 5968 5969
                      "    -h | --help             this help\n"
                      "    -q | --quiet            quiet mode\n"
                      "    -t | --timing           print timing information\n"
5970 5971 5972 5973 5974
                      "    -l | --log=FILE         output logging to file\n"
                      "    -v                      short version\n"
                      "    -V                      long version\n"
                      "         --version[=TYPE]   version, TYPE is short or long (default short)\n"
                      "    -e | --escape <char>    set escape sequence for console\n\n"
5975
                      "  commands (non interactive mode):\n\n"), progname, progname);
5976

5977
    for (grp = cmdGroups; grp->name; grp++) {
E
Eric Blake 已提交
5978 5979 5980 5981 5982
        fprintf(stdout, _(" %s (help keyword '%s')\n"),
                grp->name, grp->keyword);
        for (cmd = grp->commands; cmd->name; cmd++) {
            if (cmd->flags & VSH_CMD_FLAG_ALIAS)
                continue;
5983
            fprintf(stdout,
E
Eric Blake 已提交
5984 5985 5986
                    "    %-30s %s\n", cmd->name,
                    _(vshCmddefGetInfo(cmd, "help")));
        }
5987 5988 5989 5990 5991
        fprintf(stdout, "\n");
    }

    fprintf(stdout, "%s",
            _("\n  (specify help <group> for details about the commands in the group)\n"));
5992 5993 5994
    fprintf(stdout, "%s",
            _("\n  (specify help <command> for details about the command)\n\n"));
    return;
K
Karel Zak 已提交
5995 5996
}

5997 5998 5999 6000 6001 6002 6003 6004 6005 6006
/*
 * Show version and options compiled in
 */
static void
vshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
{
    /* FIXME - list a copyright blurb, as in GNU programs?  */
    vshPrint(ctl, _("Virsh command line tool of libvirt %s\n"), VERSION);
    vshPrint(ctl, _("See web site at %s\n\n"), "http://libvirt.org/");

L
Laine Stump 已提交
6007 6008
    vshPrint(ctl, "%s", _("Compiled with support for:\n"));
    vshPrint(ctl, "%s", _(" Hypervisors:"));
6009 6010 6011
#ifdef WITH_QEMU
    vshPrint(ctl, " QEmu/KVM");
#endif
D
Doug Goldstein 已提交
6012 6013 6014
#ifdef WITH_LXC
    vshPrint(ctl, " LXC");
#endif
6015 6016 6017
#ifdef WITH_UML
    vshPrint(ctl, " UML");
#endif
D
Doug Goldstein 已提交
6018 6019 6020 6021 6022 6023
#ifdef WITH_XEN
    vshPrint(ctl, " Xen");
#endif
#ifdef WITH_LIBXL
    vshPrint(ctl, " LibXL");
#endif
6024 6025 6026
#ifdef WITH_OPENVZ
    vshPrint(ctl, " OpenVZ");
#endif
D
Doug Goldstein 已提交
6027 6028
#ifdef WITH_VMWARE
    vshPrint(ctl, " VMWare");
6029
#endif
D
Doug Goldstein 已提交
6030 6031
#ifdef WITH_PHYP
    vshPrint(ctl, " PHYP");
6032
#endif
D
Doug Goldstein 已提交
6033 6034
#ifdef WITH_VBOX
    vshPrint(ctl, " VirtualBox");
6035 6036 6037 6038
#endif
#ifdef WITH_ESX
    vshPrint(ctl, " ESX");
#endif
D
Doug Goldstein 已提交
6039 6040
#ifdef WITH_HYPERV
    vshPrint(ctl, " Hyper-V");
6041
#endif
D
Doug Goldstein 已提交
6042 6043
#ifdef WITH_XENAPI
    vshPrint(ctl, " XenAPI");
6044 6045 6046 6047 6048 6049
#endif
#ifdef WITH_TEST
    vshPrint(ctl, " Test");
#endif
    vshPrint(ctl, "\n");

L
Laine Stump 已提交
6050
    vshPrint(ctl, "%s", _(" Networking:"));
6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063
#ifdef WITH_REMOTE
    vshPrint(ctl, " Remote");
#endif
#ifdef WITH_LIBVIRTD
    vshPrint(ctl, " Daemon");
#endif
#ifdef WITH_NETWORK
    vshPrint(ctl, " Network");
#endif
#ifdef WITH_BRIDGE
    vshPrint(ctl, " Bridging");
#endif
#ifdef WITH_NETCF
D
Doug Goldstein 已提交
6064
    vshPrint(ctl, " Interface");
6065 6066 6067 6068 6069 6070 6071 6072 6073
#endif
#ifdef WITH_NWFILTER
    vshPrint(ctl, " Nwfilter");
#endif
#ifdef WITH_VIRTUALPORT
    vshPrint(ctl, " VirtualPort");
#endif
    vshPrint(ctl, "\n");

L
Laine Stump 已提交
6074
    vshPrint(ctl, "%s", _(" Storage:"));
6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094
#ifdef WITH_STORAGE_DIR
    vshPrint(ctl, " Dir");
#endif
#ifdef WITH_STORAGE_DISK
    vshPrint(ctl, " Disk");
#endif
#ifdef WITH_STORAGE_FS
    vshPrint(ctl, " Filesystem");
#endif
#ifdef WITH_STORAGE_SCSI
    vshPrint(ctl, " SCSI");
#endif
#ifdef WITH_STORAGE_MPATH
    vshPrint(ctl, " Multipath");
#endif
#ifdef WITH_STORAGE_ISCSI
    vshPrint(ctl, " iSCSI");
#endif
#ifdef WITH_STORAGE_LVM
    vshPrint(ctl, " LVM");
6095 6096 6097
#endif
#ifdef WITH_STORAGE_RBD
    vshPrint(ctl, " RBD");
6098 6099 6100
#endif
#ifdef WITH_STORAGE_SHEEPDOG
    vshPrint(ctl, " Sheepdog");
6101 6102 6103
#endif
    vshPrint(ctl, "\n");

6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233
    vshPrint(ctl, "%s", _(" Miscellaneous:"));
#ifdef WITH_NODE_DEVICES
    vshPrint(ctl, " Nodedev");
#endif
#ifdef WITH_SECDRIVER_APPARMOR
    vshPrint(ctl, " AppArmor");
#endif
#ifdef WITH_SECDRIVER_SELINUX
    vshPrint(ctl, " SELinux");
#endif
#ifdef WITH_SECRETS
    vshPrint(ctl, " Secrets");
#endif
#ifdef ENABLE_DEBUG
    vshPrint(ctl, " Debug");
#endif
#ifdef WITH_DTRACE_PROBES
    vshPrint(ctl, " DTrace");
#endif
#ifdef USE_READLINE
    vshPrint(ctl, " Readline");
#endif
#ifdef WITH_DRIVER_MODULES
    vshPrint(ctl, " Modular");
#endif
    vshPrint(ctl, "\n");
}

static bool
vshAllowedEscapeChar(char c)
{
    /* Allowed escape characters:
     * a-z A-Z @ [ \ ] ^ _
     */
    return ('a' <= c && c <= 'z') ||
        ('@' <= c && c <= '_');
}

/*
 * argv[]:  virsh [options] [command]
 *
 */
static bool
vshParseArgv(vshControl *ctl, int argc, char **argv)
{
    int arg, len;
    struct option opt[] = {
        {"debug", required_argument, NULL, 'd'},
        {"help", no_argument, NULL, 'h'},
        {"quiet", no_argument, NULL, 'q'},
        {"timing", no_argument, NULL, 't'},
        {"version", optional_argument, NULL, 'v'},
        {"connect", required_argument, NULL, 'c'},
        {"readonly", no_argument, NULL, 'r'},
        {"log", required_argument, NULL, 'l'},
        {"escape", required_argument, NULL, 'e'},
        {NULL, 0, NULL, 0}
    };

    /* Standard (non-command) options. The leading + ensures that no
     * argument reordering takes place, so that command options are
     * not confused with top-level virsh options. */
    while ((arg = getopt_long(argc, argv, "+d:hqtc:vVrl:e:", opt, NULL)) != -1) {
        switch (arg) {
        case 'd':
            if (virStrToLong_i(optarg, NULL, 10, &ctl->debug) < 0) {
                vshError(ctl, "%s", _("option -d takes a numeric argument"));
                exit(EXIT_FAILURE);
            }
            break;
        case 'h':
            vshUsage();
            exit(EXIT_SUCCESS);
            break;
        case 'q':
            ctl->quiet = true;
            break;
        case 't':
            ctl->timing = true;
            break;
        case 'c':
            ctl->name = vshStrdup(ctl, optarg);
            break;
        case 'v':
            if (STRNEQ_NULLABLE(optarg, "long")) {
                puts(VERSION);
                exit(EXIT_SUCCESS);
            }
            /* fall through */
        case 'V':
            vshShowVersion(ctl);
            exit(EXIT_SUCCESS);
        case 'r':
            ctl->readonly = true;
            break;
        case 'l':
            ctl->logfile = vshStrdup(ctl, optarg);
            break;
        case 'e':
            len = strlen(optarg);

            if ((len == 2 && *optarg == '^' &&
                 vshAllowedEscapeChar(optarg[1])) ||
                (len == 1 && *optarg != '^')) {
                ctl->escapeChar = optarg;
            } else {
                vshError(ctl, _("Invalid string '%s' for escape sequence"),
                         optarg);
                exit(EXIT_FAILURE);
            }
            break;
        default:
            vshError(ctl, _("unsupported option '-%c'. See --help."), arg);
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        /* parse command */
        ctl->imode = false;
        if (argc - optind == 1) {
            vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]);
            return vshCommandStringParse(ctl, argv[optind]);
        } else {
            return vshCommandArgvParse(ctl, argc - optind, argv + optind);
        }
    }
    return true;
}

6234 6235
#include "virsh-domain.c"

6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330
static const vshCmdDef domManagementCmds[] = {
    {"attach-device", cmdAttachDevice, opts_attach_device,
     info_attach_device, 0},
    {"attach-disk", cmdAttachDisk, opts_attach_disk,
     info_attach_disk, 0},
    {"attach-interface", cmdAttachInterface, opts_attach_interface,
     info_attach_interface, 0},
    {"autostart", cmdAutostart, opts_autostart, info_autostart, 0},
    {"blkdeviotune", cmdBlkdeviotune, opts_blkdeviotune, info_blkdeviotune, 0},
    {"blkiotune", cmdBlkiotune, opts_blkiotune, info_blkiotune, 0},
    {"blockcopy", cmdBlockCopy, opts_block_copy, info_block_copy, 0},
    {"blockjob", cmdBlockJob, opts_block_job, info_block_job, 0},
    {"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0},
    {"blockresize", cmdBlockResize, opts_block_resize, info_block_resize, 0},
    {"change-media", cmdChangeMedia, opts_change_media, info_change_media, 0},
#ifndef WIN32
    {"console", cmdConsole, opts_console, info_console, 0},
#endif
    {"cpu-baseline", cmdCPUBaseline, opts_cpu_baseline, info_cpu_baseline, 0},
    {"cpu-compare", cmdCPUCompare, opts_cpu_compare, info_cpu_compare, 0},
    {"cpu-stats", cmdCPUStats, opts_cpu_stats, info_cpu_stats, 0},
    {"create", cmdCreate, opts_create, info_create, 0},
    {"define", cmdDefine, opts_define, info_define, 0},
    {"desc", cmdDesc, opts_desc, info_desc, 0},
    {"destroy", cmdDestroy, opts_destroy, info_destroy, 0},
    {"detach-device", cmdDetachDevice, opts_detach_device,
     info_detach_device, 0},
    {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk, 0},
    {"detach-interface", cmdDetachInterface, opts_detach_interface,
     info_detach_interface, 0},
    {"domdisplay", cmdDomDisplay, opts_domdisplay, info_domdisplay, 0},
    {"domhostname", cmdDomHostname, opts_domhostname, info_domhostname, 0},
    {"domid", cmdDomid, opts_domid, info_domid, 0},
    {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink, 0},
    {"domiftune", cmdDomIftune, opts_domiftune, info_domiftune, 0},
    {"domjobabort", cmdDomjobabort, opts_domjobabort, info_domjobabort, 0},
    {"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo, 0},
    {"domname", cmdDomname, opts_domname, info_domname, 0},
    {"dompmsuspend", cmdDomPMSuspend,
     opts_dom_pm_suspend, info_dom_pm_suspend, 0},
    {"dompmwakeup", cmdDomPMWakeup,
     opts_dom_pm_wakeup, info_dom_pm_wakeup, 0},
    {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid, 0},
    {"domxml-from-native", cmdDomXMLFromNative, opts_domxmlfromnative,
     info_domxmlfromnative, 0},
    {"domxml-to-native", cmdDomXMLToNative, opts_domxmltonative,
     info_domxmltonative, 0},
    {"dump", cmdDump, opts_dump, info_dump, 0},
    {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0},
    {"edit", cmdEdit, opts_edit, info_edit, 0},
    {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0},
    {"send-key", cmdSendKey, opts_send_key, info_send_key, 0},
    {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0},
    {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove,
     info_managedsaveremove, 0},
    {"maxvcpus", cmdMaxvcpus, opts_maxvcpus, info_maxvcpus, 0},
    {"memtune", cmdMemtune, opts_memtune, info_memtune, 0},
    {"migrate", cmdMigrate, opts_migrate, info_migrate, 0},
    {"migrate-setmaxdowntime", cmdMigrateSetMaxDowntime,
     opts_migrate_setmaxdowntime, info_migrate_setmaxdowntime, 0},
    {"migrate-setspeed", cmdMigrateSetMaxSpeed,
     opts_migrate_setspeed, info_migrate_setspeed, 0},
    {"migrate-getspeed", cmdMigrateGetMaxSpeed,
     opts_migrate_getspeed, info_migrate_getspeed, 0},
    {"numatune", cmdNumatune, opts_numatune, info_numatune, 0},
    {"reboot", cmdReboot, opts_reboot, info_reboot, 0},
    {"reset", cmdReset, opts_reset, info_reset, 0},
    {"restore", cmdRestore, opts_restore, info_restore, 0},
    {"resume", cmdResume, opts_resume, info_resume, 0},
    {"save", cmdSave, opts_save, info_save, 0},
    {"save-image-define", cmdSaveImageDefine, opts_save_image_define,
     info_save_image_define, 0},
    {"save-image-dumpxml", cmdSaveImageDumpxml, opts_save_image_dumpxml,
     info_save_image_dumpxml, 0},
    {"save-image-edit", cmdSaveImageEdit, opts_save_image_edit,
     info_save_image_edit, 0},
    {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo, 0},
    {"screenshot", cmdScreenshot, opts_screenshot, info_screenshot, 0},
    {"setmaxmem", cmdSetmaxmem, opts_setmaxmem, info_setmaxmem, 0},
    {"setmem", cmdSetmem, opts_setmem, info_setmem, 0},
    {"setvcpus", cmdSetvcpus, opts_setvcpus, info_setvcpus, 0},
    {"shutdown", cmdShutdown, opts_shutdown, info_shutdown, 0},
    {"start", cmdStart, opts_start, info_start, 0},
    {"suspend", cmdSuspend, opts_suspend, info_suspend, 0},
    {"ttyconsole", cmdTTYConsole, opts_ttyconsole, info_ttyconsole, 0},
    {"undefine", cmdUndefine, opts_undefine, info_undefine, 0},
    {"update-device", cmdUpdateDevice, opts_update_device,
     info_update_device, 0},
    {"vcpucount", cmdVcpucount, opts_vcpucount, info_vcpucount, 0},
    {"vcpuinfo", cmdVcpuinfo, opts_vcpuinfo, info_vcpuinfo, 0},
    {"vcpupin", cmdVcpuPin, opts_vcpupin, info_vcpupin, 0},
    {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay, 0},
    {NULL, NULL, NULL, NULL, 0}
};

6331 6332
#include "virsh-domain-monitor.c"

6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348
static const vshCmdDef domMonitoringCmds[] = {
    {"domblkerror", cmdDomBlkError, opts_domblkerror, info_domblkerror, 0},
    {"domblkinfo", cmdDomblkinfo, opts_domblkinfo, info_domblkinfo, 0},
    {"domblklist", cmdDomblklist, opts_domblklist, info_domblklist, 0},
    {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat, 0},
    {"domcontrol", cmdDomControl, opts_domcontrol, info_domcontrol, 0},
    {"domif-getlink", cmdDomIfGetLink, opts_domif_getlink, info_domif_getlink, 0},
    {"domiflist", cmdDomiflist, opts_domiflist, info_domiflist, 0},
    {"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat, 0},
    {"dominfo", cmdDominfo, opts_dominfo, info_dominfo, 0},
    {"dommemstat", cmdDomMemStat, opts_dommemstat, info_dommemstat, 0},
    {"domstate", cmdDomstate, opts_domstate, info_domstate, 0},
    {"list", cmdList, opts_list, info_list, 0},
    {NULL, NULL, NULL, NULL, 0}
};

6349 6350
#include "virsh-pool.c"

6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377
static const vshCmdDef storagePoolCmds[] = {
    {"find-storage-pool-sources-as", cmdPoolDiscoverSourcesAs,
     opts_find_storage_pool_sources_as, info_find_storage_pool_sources_as, 0},
    {"find-storage-pool-sources", cmdPoolDiscoverSources,
     opts_find_storage_pool_sources, info_find_storage_pool_sources, 0},
    {"pool-autostart", cmdPoolAutostart, opts_pool_autostart,
     info_pool_autostart, 0},
    {"pool-build", cmdPoolBuild, opts_pool_build, info_pool_build, 0},
    {"pool-create-as", cmdPoolCreateAs, opts_pool_X_as, info_pool_create_as, 0},
    {"pool-create", cmdPoolCreate, opts_pool_create, info_pool_create, 0},
    {"pool-define-as", cmdPoolDefineAs, opts_pool_X_as, info_pool_define_as, 0},
    {"pool-define", cmdPoolDefine, opts_pool_define, info_pool_define, 0},
    {"pool-delete", cmdPoolDelete, opts_pool_delete, info_pool_delete, 0},
    {"pool-destroy", cmdPoolDestroy, opts_pool_destroy, info_pool_destroy, 0},
    {"pool-dumpxml", cmdPoolDumpXML, opts_pool_dumpxml, info_pool_dumpxml, 0},
    {"pool-edit", cmdPoolEdit, opts_pool_edit, info_pool_edit, 0},
    {"pool-info", cmdPoolInfo, opts_pool_info, info_pool_info, 0},
    {"pool-list", cmdPoolList, opts_pool_list, info_pool_list, 0},
    {"pool-name", cmdPoolName, opts_pool_name, info_pool_name, 0},
    {"pool-refresh", cmdPoolRefresh, opts_pool_refresh, info_pool_refresh, 0},
    {"pool-start", cmdPoolStart, opts_pool_start, info_pool_start, 0},
    {"pool-undefine", cmdPoolUndefine, opts_pool_undefine,
     info_pool_undefine, 0},
    {"pool-uuid", cmdPoolUuid, opts_pool_uuid, info_pool_uuid, 0},
    {NULL, NULL, NULL, NULL, 0}
};

6378 6379
#include "virsh-volume.c"

6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401
static const vshCmdDef storageVolCmds[] = {
    {"vol-clone", cmdVolClone, opts_vol_clone, info_vol_clone, 0},
    {"vol-create-as", cmdVolCreateAs, opts_vol_create_as,
     info_vol_create_as, 0},
    {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create, 0},
    {"vol-create-from", cmdVolCreateFrom, opts_vol_create_from,
     info_vol_create_from, 0},
    {"vol-delete", cmdVolDelete, opts_vol_delete, info_vol_delete, 0},
    {"vol-download", cmdVolDownload, opts_vol_download, info_vol_download, 0},
    {"vol-dumpxml", cmdVolDumpXML, opts_vol_dumpxml, info_vol_dumpxml, 0},
    {"vol-info", cmdVolInfo, opts_vol_info, info_vol_info, 0},
    {"vol-key", cmdVolKey, opts_vol_key, info_vol_key, 0},
    {"vol-list", cmdVolList, opts_vol_list, info_vol_list, 0},
    {"vol-name", cmdVolName, opts_vol_name, info_vol_name, 0},
    {"vol-path", cmdVolPath, opts_vol_path, info_vol_path, 0},
    {"vol-pool", cmdVolPool, opts_vol_pool, info_vol_pool, 0},
    {"vol-resize", cmdVolResize, opts_vol_resize, info_vol_resize, 0},
    {"vol-upload", cmdVolUpload, opts_vol_upload, info_vol_upload, 0},
    {"vol-wipe", cmdVolWipe, opts_vol_wipe, info_vol_wipe, 0},
    {NULL, NULL, NULL, NULL, 0}
};

6402 6403
#include "virsh-network.c"

6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445
static const vshCmdDef networkCmds[] = {
    {"net-autostart", cmdNetworkAutostart, opts_network_autostart,
     info_network_autostart, 0},
    {"net-create", cmdNetworkCreate, opts_network_create,
     info_network_create, 0},
    {"net-define", cmdNetworkDefine, opts_network_define,
     info_network_define, 0},
    {"net-destroy", cmdNetworkDestroy, opts_network_destroy,
     info_network_destroy, 0},
    {"net-dumpxml", cmdNetworkDumpXML, opts_network_dumpxml,
     info_network_dumpxml, 0},
    {"net-edit", cmdNetworkEdit, opts_network_edit, info_network_edit, 0},
    {"net-info", cmdNetworkInfo, opts_network_info, info_network_info, 0},
    {"net-list", cmdNetworkList, opts_network_list, info_network_list, 0},
    {"net-name", cmdNetworkName, opts_network_name, info_network_name, 0},
    {"net-start", cmdNetworkStart, opts_network_start, info_network_start, 0},
    {"net-undefine", cmdNetworkUndefine, opts_network_undefine,
     info_network_undefine, 0},
    {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid, 0},
    {NULL, NULL, NULL, NULL, 0}
};

static const vshCmdDef nodedevCmds[] = {
    {"nodedev-create", cmdNodeDeviceCreate, opts_node_device_create,
     info_node_device_create, 0},
    {"nodedev-destroy", cmdNodeDeviceDestroy, opts_node_device_destroy,
     info_node_device_destroy, 0},
    {"nodedev-detach", cmdNodeDeviceDetach, opts_node_device_detach,
     info_node_device_detach, 0},
    {"nodedev-dettach", cmdNodeDeviceDetach, opts_node_device_detach,
     info_node_device_detach, VSH_CMD_FLAG_ALIAS},
    {"nodedev-dumpxml", cmdNodeDeviceDumpXML, opts_node_device_dumpxml,
     info_node_device_dumpxml, 0},
    {"nodedev-list", cmdNodeListDevices, opts_node_list_devices,
     info_node_list_devices, 0},
    {"nodedev-reattach", cmdNodeDeviceReAttach, opts_node_device_reattach,
     info_node_device_reattach, 0},
    {"nodedev-reset", cmdNodeDeviceReset, opts_node_device_reset,
     info_node_device_reset, 0},
    {NULL, NULL, NULL, NULL, 0}
};

6446 6447
#include "virsh-interface.c"

6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478
static const vshCmdDef ifaceCmds[] = {
    {"iface-begin", cmdInterfaceBegin, opts_interface_begin,
     info_interface_begin, 0},
    {"iface-bridge", cmdInterfaceBridge, opts_interface_bridge,
     info_interface_bridge, 0},
    {"iface-commit", cmdInterfaceCommit, opts_interface_commit,
     info_interface_commit, 0},
    {"iface-define", cmdInterfaceDefine, opts_interface_define,
     info_interface_define, 0},
    {"iface-destroy", cmdInterfaceDestroy, opts_interface_destroy,
     info_interface_destroy, 0},
    {"iface-dumpxml", cmdInterfaceDumpXML, opts_interface_dumpxml,
     info_interface_dumpxml, 0},
    {"iface-edit", cmdInterfaceEdit, opts_interface_edit,
     info_interface_edit, 0},
    {"iface-list", cmdInterfaceList, opts_interface_list,
     info_interface_list, 0},
    {"iface-mac", cmdInterfaceMAC, opts_interface_mac,
     info_interface_mac, 0},
    {"iface-name", cmdInterfaceName, opts_interface_name,
     info_interface_name, 0},
    {"iface-rollback", cmdInterfaceRollback, opts_interface_rollback,
     info_interface_rollback, 0},
    {"iface-start", cmdInterfaceStart, opts_interface_start,
     info_interface_start, 0},
    {"iface-unbridge", cmdInterfaceUnbridge, opts_interface_unbridge,
     info_interface_unbridge, 0},
    {"iface-undefine", cmdInterfaceUndefine, opts_interface_undefine,
     info_interface_undefine, 0},
    {NULL, NULL, NULL, NULL, 0}
};
6479

6480 6481
#include "virsh-nwfilter.c"

6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494
static const vshCmdDef nwfilterCmds[] = {
    {"nwfilter-define", cmdNWFilterDefine, opts_nwfilter_define,
     info_nwfilter_define, 0},
    {"nwfilter-dumpxml", cmdNWFilterDumpXML, opts_nwfilter_dumpxml,
     info_nwfilter_dumpxml, 0},
    {"nwfilter-edit", cmdNWFilterEdit, opts_nwfilter_edit,
     info_nwfilter_edit, 0},
    {"nwfilter-list", cmdNWFilterList, opts_nwfilter_list,
     info_nwfilter_list, 0},
    {"nwfilter-undefine", cmdNWFilterUndefine, opts_nwfilter_undefine,
     info_nwfilter_undefine, 0},
    {NULL, NULL, NULL, NULL, 0}
};
6495

6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509
static const vshCmdDef secretCmds[] = {
    {"secret-define", cmdSecretDefine, opts_secret_define,
     info_secret_define, 0},
    {"secret-dumpxml", cmdSecretDumpXML, opts_secret_dumpxml,
     info_secret_dumpxml, 0},
    {"secret-get-value", cmdSecretGetValue, opts_secret_get_value,
     info_secret_get_value, 0},
    {"secret-list", cmdSecretList, NULL, info_secret_list, 0},
    {"secret-set-value", cmdSecretSetValue, opts_secret_set_value,
     info_secret_set_value, 0},
    {"secret-undefine", cmdSecretUndefine, opts_secret_undefine,
     info_secret_undefine, 0},
    {NULL, NULL, NULL, NULL, 0}
};
6510

6511 6512 6513 6514 6515 6516 6517 6518 6519
static const vshCmdDef virshCmds[] = {
    {"cd", cmdCd, opts_cd, info_cd, VSH_CMD_FLAG_NOCONNECT},
    {"echo", cmdEcho, opts_echo, info_echo, VSH_CMD_FLAG_NOCONNECT},
    {"exit", cmdQuit, NULL, info_quit, VSH_CMD_FLAG_NOCONNECT},
    {"help", cmdHelp, opts_help, info_help, VSH_CMD_FLAG_NOCONNECT},
    {"pwd", cmdPwd, NULL, info_pwd, VSH_CMD_FLAG_NOCONNECT},
    {"quit", cmdQuit, NULL, info_quit, VSH_CMD_FLAG_NOCONNECT},
    {NULL, NULL, NULL, NULL, 0}
};
6520

6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543
static const vshCmdDef snapshotCmds[] = {
    {"snapshot-create", cmdSnapshotCreate, opts_snapshot_create,
     info_snapshot_create, 0},
    {"snapshot-create-as", cmdSnapshotCreateAs, opts_snapshot_create_as,
     info_snapshot_create_as, 0},
    {"snapshot-current", cmdSnapshotCurrent, opts_snapshot_current,
     info_snapshot_current, 0},
    {"snapshot-delete", cmdSnapshotDelete, opts_snapshot_delete,
     info_snapshot_delete, 0},
    {"snapshot-dumpxml", cmdSnapshotDumpXML, opts_snapshot_dumpxml,
     info_snapshot_dumpxml, 0},
    {"snapshot-edit", cmdSnapshotEdit, opts_snapshot_edit,
     info_snapshot_edit, 0},
    {"snapshot-info", cmdSnapshotInfo, opts_snapshot_info,
     info_snapshot_info, 0},
    {"snapshot-list", cmdSnapshotList, opts_snapshot_list,
     info_snapshot_list, 0},
    {"snapshot-parent", cmdSnapshotParent, opts_snapshot_parent,
     info_snapshot_parent, 0},
    {"snapshot-revert", cmdDomainSnapshotRevert, opts_snapshot_revert,
     info_snapshot_revert, 0},
    {NULL, NULL, NULL, NULL, 0}
};
K
Karel Zak 已提交
6544

6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578
static const vshCmdDef hostAndHypervisorCmds[] = {
    {"capabilities", cmdCapabilities, NULL, info_capabilities, 0},
    {"connect", cmdConnect, opts_connect, info_connect,
     VSH_CMD_FLAG_NOCONNECT},
    {"freecell", cmdFreecell, opts_freecell, info_freecell, 0},
    {"hostname", cmdHostname, NULL, info_hostname, 0},
    {"nodecpustats", cmdNodeCpuStats, opts_node_cpustats, info_nodecpustats, 0},
    {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo, 0},
    {"nodememstats", cmdNodeMemStats, opts_node_memstats, info_nodememstats, 0},
    {"nodesuspend", cmdNodeSuspend, opts_node_suspend, info_nodesuspend, 0},
    {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0},
    {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command,
     info_qemu_monitor_command, 0},
    {"sysinfo", cmdSysinfo, NULL, info_sysinfo, 0},
    {"uri", cmdURI, NULL, info_uri, 0},
    {"version", cmdVersion, opts_version, info_version, 0},
    {NULL, NULL, NULL, NULL, 0}
};

static const vshCmdGrp cmdGroups[] = {
    {VSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds},
    {VSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds},
    {VSH_CMD_GRP_HOST_AND_HV, "host", hostAndHypervisorCmds},
    {VSH_CMD_GRP_IFACE, "interface", ifaceCmds},
    {VSH_CMD_GRP_NWFILTER, "filter", nwfilterCmds},
    {VSH_CMD_GRP_NETWORK, "network", networkCmds},
    {VSH_CMD_GRP_NODEDEV, "nodedev", nodedevCmds},
    {VSH_CMD_GRP_SECRET, "secret", secretCmds},
    {VSH_CMD_GRP_SNAPSHOT, "snapshot", snapshotCmds},
    {VSH_CMD_GRP_STORAGE_POOL, "pool", storagePoolCmds},
    {VSH_CMD_GRP_STORAGE_VOL, "volume", storageVolCmds},
    {VSH_CMD_GRP_VIRSH, "virsh", virshCmds},
    {NULL, NULL, NULL}
};
K
Karel Zak 已提交
6579

6580 6581 6582 6583
int
main(int argc, char **argv)
{
    vshControl _ctl, *ctl = &_ctl;
6584
    char *defaultConn;
E
Eric Blake 已提交
6585
    bool ret = true;
K
Karel Zak 已提交
6586

6587 6588 6589
    memset(ctl, 0, sizeof(vshControl));
    ctl->imode = true;          /* default is interactive mode */
    ctl->log_fd = -1;           /* Initialize log file descriptor */
J
Jiri Denemark 已提交
6590
    ctl->debug = VSH_DEBUG_DEFAULT;
6591 6592
    ctl->escapeChar = CTRL_CLOSE_BRACKET;

6593

6594 6595
    if (!setlocale(LC_ALL, "")) {
        perror("setlocale");
6596
        /* failure to setup locale is not fatal */
6597
    }
6598
    if (!bindtextdomain(PACKAGE, LOCALEDIR)) {
6599
        perror("bindtextdomain");
E
Eric Blake 已提交
6600
        return EXIT_FAILURE;
6601
    }
6602
    if (!textdomain(PACKAGE)) {
6603
        perror("textdomain");
E
Eric Blake 已提交
6604
        return EXIT_FAILURE;
6605 6606
    }

6607 6608 6609 6610 6611
    if (virMutexInit(&ctl->lock) < 0) {
        vshError(ctl, "%s", _("Failed to initialize mutex"));
        return EXIT_FAILURE;
    }

6612 6613 6614 6615 6616
    if (virInitialize() < 0) {
        vshError(ctl, "%s", _("Failed to initialize libvirt"));
        return EXIT_FAILURE;
    }

6617
    if (!(progname = strrchr(argv[0], '/')))
K
Karel Zak 已提交
6618 6619 6620
        progname = argv[0];
    else
        progname++;
6621

6622
    if ((defaultConn = getenv("VIRSH_DEFAULT_CONNECT_URI"))) {
E
Eric Blake 已提交
6623
        ctl->name = vshStrdup(ctl, defaultConn);
6624 6625
    }

D
Daniel P. Berrange 已提交
6626 6627
    if (!vshParseArgv(ctl, argc, argv)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
6628
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
6629
    }
6630

D
Daniel P. Berrange 已提交
6631 6632
    if (!vshInit(ctl)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
6633
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
6634
    }
6635

K
Karel Zak 已提交
6636
    if (!ctl->imode) {
6637
        ret = vshCommandRun(ctl, ctl->cmd);
6638
    } else {
K
Karel Zak 已提交
6639 6640
        /* interactive mode */
        if (!ctl->quiet) {
K
Karel Zak 已提交
6641
            vshPrint(ctl,
6642
                     _("Welcome to %s, the virtualization interactive terminal.\n\n"),
6643
                     progname);
J
Jim Meyering 已提交
6644
            vshPrint(ctl, "%s",
6645
                     _("Type:  'help' for help with commands\n"
6646
                       "       'quit' to quit\n\n"));
K
Karel Zak 已提交
6647
        }
6648 6649 6650 6651 6652 6653

        if (vshReadlineInit(ctl) < 0) {
            vshDeinit(ctl);
            exit(EXIT_FAILURE);
        }

K
Karel Zak 已提交
6654
        do {
6655
            const char *prompt = ctl->readonly ? VSH_PROMPT_RO : VSH_PROMPT_RW;
6656
            ctl->cmdstr =
6657
                vshReadline(ctl, prompt);
6658 6659
            if (ctl->cmdstr == NULL)
                break;          /* EOF */
K
Karel Zak 已提交
6660
            if (*ctl->cmdstr) {
6661
#if USE_READLINE
K
Karel Zak 已提交
6662
                add_history(ctl->cmdstr);
6663
#endif
6664
                if (vshCommandStringParse(ctl, ctl->cmdstr))
K
Karel Zak 已提交
6665 6666
                    vshCommandRun(ctl, ctl->cmd);
            }
6667
            VIR_FREE(ctl->cmdstr);
6668
        } while (ctl->imode);
K
Karel Zak 已提交
6669

6670 6671
        if (ctl->cmdstr == NULL)
            fputc('\n', stdout);        /* line break after alone prompt */
K
Karel Zak 已提交
6672
    }
6673

K
Karel Zak 已提交
6674 6675
    vshDeinit(ctl);
    exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
6676
}