virsh.c 581.5 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>
K
Karel Zak 已提交
36

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

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

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

static char *progname;

69 70
#define VIRSH_MAX_XML_FILE 10*1024*1024

K
Karel Zak 已提交
71 72 73
#define VSH_PROMPT_RW    "virsh # "
#define VSH_PROMPT_RO    "virsh > "

74 75
#define VIR_FROM_THIS VIR_FROM_NONE

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

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

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

J
Jiri Denemark 已提交
111 112
#define VSH_DEBUG_DEFAULT VSH_ERR_ERROR

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

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

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
/*
 * 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 已提交
161 162 163
/*
 * Command Option Flags
 */
E
Eric Blake 已提交
164 165 166 167
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 已提交
168
    VSH_OFLAG_REQ_OPT  = (1 << 2), /* --optionname required */
E
Eric Blake 已提交
169
};
K
Karel Zak 已提交
170 171 172 173 174 175

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

/*
E
Eric Blake 已提交
176 177 178 179 180
 * 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 已提交
181
 */
182
typedef struct {
E
Eric Blake 已提交
183 184
    const char *name;           /* name of information, or NULL for list end */
    const char *data;           /* non-NULL information */
K
Karel Zak 已提交
185 186 187 188 189
} vshCmdInfo;

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

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

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

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

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

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

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

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

276
static const vshCmdGrp cmdGroups[];
K
Karel Zak 已提交
277

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

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

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

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

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

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

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

339
static virNetworkPtr vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
340
                                            const char **name, int flag);
341 342

/* default is lookup by Name and UUID */
J
Jim Meyering 已提交
343 344
#define vshCommandOptNetwork(_ctl, _cmd, _name)                    \
    vshCommandOptNetworkBy(_ctl, _cmd, _name,                      \
345 346
                           VSH_BYUUID|VSH_BYNAME)

347
static virNWFilterPtr vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd,
348
                                                  const char **name, int flag);
349 350 351 352 353 354

/* default is lookup by Name and UUID */
#define vshCommandOptNWFilter(_ctl, _cmd, _name)                    \
    vshCommandOptNWFilterBy(_ctl, _cmd, _name,                      \
                            VSH_BYUUID|VSH_BYNAME)

355
static virInterfacePtr vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd,
356
                                                const char *optname,
357
                                                const char **name, int flag);
358 359 360

/* default is lookup by Name and MAC */
#define vshCommandOptInterface(_ctl, _cmd, _name)                    \
361 362
    vshCommandOptInterfaceBy(_ctl, _cmd, NULL, _name,                \
                             VSH_BYMAC|VSH_BYNAME)
363

364
static virStoragePoolPtr vshCommandOptPoolBy(vshControl *ctl, const vshCmd *cmd,
365
                            const char *optname, const char **name, int flag);
366 367 368 369 370 371

/* default is lookup by Name and UUID */
#define vshCommandOptPool(_ctl, _cmd, _optname, _name)           \
    vshCommandOptPoolBy(_ctl, _cmd, _optname, _name,             \
                           VSH_BYUUID|VSH_BYNAME)

372
static virStorageVolPtr vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd,
373 374
                                           const char *optname,
                                           const char *pooloptname,
375
                                           const char **name, int flag);
376 377

/* default is lookup by Name and UUID */
378
#define vshCommandOptVol(_ctl, _cmd, _optname, _pooloptname, _name)   \
379 380 381
    vshCommandOptVolBy(_ctl, _cmd, _optname, _pooloptname, _name,     \
                           VSH_BYUUID|VSH_BYNAME)

382
static virSecretPtr vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd,
383
                                        const char **name);
384

385
static void vshPrintExtra(vshControl *ctl, const char *format, ...)
386
    ATTRIBUTE_FMT_PRINTF(2, 3);
387
static void vshDebug(vshControl *ctl, int level, const char *format, ...)
388
    ATTRIBUTE_FMT_PRINTF(3, 4);
K
Karel Zak 已提交
389 390

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

393
static int vshDomainState(vshControl *ctl, virDomainPtr dom, int *reason);
K
Karel Zak 已提交
394
static const char *vshDomainStateToString(int state);
395
static const char *vshDomainStateReasonToString(int state, int reason);
396
static const char *vshDomainControlStateToString(int state);
397
static const char *vshDomainVcpuStateToString(int state);
E
Eric Blake 已提交
398
static bool vshConnectionUsability(vshControl *ctl, virConnectPtr conn);
399 400 401
static virTypedParameterPtr vshFindTypedParamByName(const char *name,
                                                    virTypedParameterPtr list,
                                                    int count);
402 403
static char *vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
K
Karel Zak 已提交
404

405 406 407 408
static char *editWriteToTempFile (vshControl *ctl, const char *doc);
static int   editFile (vshControl *ctl, const char *filename);
static char *editReadBackFile (vshControl *ctl, const char *filename);

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
/* 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);

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

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

438
static char *_vshStrdup(vshControl *ctl, const char *s, const char *filename, int line);
439 440
#define vshStrdup(_ctl, _s)    _vshStrdup(_ctl, _s, __FILE__, __LINE__)

441 442
static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate);

E
Eric Blake 已提交
443 444 445
static void *
_vshMalloc(vshControl *ctl, size_t size, const char *filename, int line)
{
E
Eric Blake 已提交
446
    char *x;
E
Eric Blake 已提交
447

E
Eric Blake 已提交
448
    if (VIR_ALLOC_N(x, size) == 0)
E
Eric Blake 已提交
449 450 451 452 453 454 455 456 457
        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 已提交
458
    char *x;
E
Eric Blake 已提交
459

E
Eric Blake 已提交
460 461
    if (!xalloc_oversized(nmemb, size) &&
        VIR_ALLOC_N(x, nmemb * size) == 0)
E
Eric Blake 已提交
462 463 464 465 466 467 468 469 470 471 472 473
        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)
474
        return NULL;
E
Eric Blake 已提交
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
    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
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505

static int idsorter(const void *a, const void *b) {
  const int *ia = (const int *)a;
  const int *ib = (const int *)b;

  if (*ia > *ib)
    return 1;
  else if (*ia < *ib)
    return -1;
  return 0;
}
static int namesorter(const void *a, const void *b) {
  const char **sa = (const char**)a;
  const char **sb = (const char**)b;

506
  /* User visible sort, so we want locale-specific case comparison.  */
507 508 509
  return strcasecmp(*sa, *sb);
}

510 511 512 513 514 515 516 517 518 519 520
static double
prettyCapacity(unsigned long long val,
               const char **unit) {
    if (val < 1024) {
        *unit = "";
        return (double)val;
    } else if (val < (1024.0l * 1024.0l)) {
        *unit = "KB";
        return (((double)val / 1024.0l));
    } else if (val < (1024.0l * 1024.0l * 1024.0l)) {
        *unit = "MB";
521
        return (double)val / (1024.0l * 1024.0l);
522 523
    } else if (val < (1024.0l * 1024.0l * 1024.0l * 1024.0l)) {
        *unit = "GB";
524
        return (double)val / (1024.0l * 1024.0l * 1024.0l);
525 526
    } else {
        *unit = "TB";
527
        return (double)val / (1024.0l * 1024.0l * 1024.0l * 1024.0l);
528 529 530 531
    }
}


J
John Levon 已提交
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
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)
{
557 558 559 560 561 562 563 564
    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)
565
            goto out;
566
    }
J
John Levon 已提交
567 568

    if (last_error->code == VIR_ERR_OK) {
569
        vshError(ctl, "%s", _("unknown error"));
J
John Levon 已提交
570 571 572
        goto out;
    }

573
    vshError(ctl, "%s", last_error->message);
J
John Levon 已提交
574 575 576 577 578 579

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

580 581 582 583 584 585 586 587 588
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;
}

589 590 591 592 593
/*
 * Detection of disconnections and automatic reconnection support
 */
static int disconnected = 0; /* we may have been disconnected */

594 595 596 597 598
/* Gnulib doesn't guarantee SA_SIGINFO support.  */
#ifndef SA_SIGINFO
# define SA_SIGINFO 0
#endif

599 600 601 602 603 604
/*
 * 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
 */
605 606 607 608
static void vshCatchDisconnect(int sig, siginfo_t *siginfo,
                               void *context ATTRIBUTE_UNUSED) {
    if ((sig == SIGPIPE) ||
        (SA_SIGINFO && siginfo->si_signo == SIGPIPE))
609 610 611 612 613 614 615 616 617
        disconnected++;
}

/*
 * vshSetupSignals:
 *
 * Catch SIGPIPE signals which may arise when disconnection
 * from libvirtd occurs
 */
L
Laine Stump 已提交
618
static void
619 620 621 622 623 624 625 626 627 628 629 630 631
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 已提交
632
 * Reconnect after a disconnect from libvirtd
633 634
 *
 */
L
Laine Stump 已提交
635
static void
636 637 638 639 640 641
vshReconnect(vshControl *ctl)
{
    bool connected = false;

    if (ctl->conn != NULL) {
        connected = true;
642
        virConnectClose(ctl->conn);
643
    }
644 645 646 647 648 649

    ctl->conn = virConnectOpenAuth(ctl->name,
                                   virConnectAuthPtrDefault,
                                   ctl->readonly ? VIR_CONNECT_RO : 0);
    if (!ctl->conn)
        vshError(ctl, "%s", _("Failed to reconnect to the hypervisor"));
650
    else if (connected)
651 652
        vshError(ctl, "%s", _("Reconnected to the hypervisor"));
    disconnected = 0;
653
    ctl->useGetInfo = false;
654
    ctl->useSnapshotOld = false;
655
}
656

K
Karel Zak 已提交
657 658 659 660 661 662
/* ---------------
 * Commands
 * ---------------
 */

/*
663
 * "help" command
K
Karel Zak 已提交
664
 */
665
static const vshCmdInfo info_help[] = {
666
    {"help", N_("print help")},
667 668
    {"desc", N_("Prints global help, command specific help, or help for a\n"
                "    group of related commands")},
669

670
    {NULL, NULL}
K
Karel Zak 已提交
671 672
};

673
static const vshCmdOptDef opts_help[] = {
674
    {"command", VSH_OT_DATA, 0, N_("Prints global help, command specific help, or help for a group of related commands")},
675
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
676 677
};

E
Eric Blake 已提交
678
static bool
679
cmdHelp(vshControl *ctl, const vshCmd *cmd)
680
 {
681
    const char *name = NULL;
682

683
    if (vshCommandOptString(cmd, "command", &name) <= 0) {
684
        const vshCmdGrp *grp;
685
        const vshCmdDef *def;
686

687 688 689 690 691 692
        vshPrint(ctl, "%s", _("Grouped commands:\n\n"));

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

693 694 695
            for (def = grp->commands; def->name; def++) {
                if (def->flags & VSH_CMD_FLAG_ALIAS)
                    continue;
696 697
                vshPrint(ctl, "    %-30s %s\n", def->name,
                         _(vshCmddefGetInfo(def, "help")));
698
            }
699 700 701 702

            vshPrint(ctl, "\n");
        }

E
Eric Blake 已提交
703
        return true;
704
    }
705

E
Eric Blake 已提交
706
    if (vshCmddefSearch(name)) {
707
        return vshCmddefHelp(ctl, name);
E
Eric Blake 已提交
708
    } else if (vshCmdGrpSearch(name)) {
709 710 711
        return vshCmdGrpHelp(ctl, name);
    } else {
        vshError(ctl, _("command or command group '%s' doesn't exist"), name);
E
Eric Blake 已提交
712
        return false;
K
Karel Zak 已提交
713 714 715
    }
}

716 717 718
/*
 * "autostart" command
 */
719
static const vshCmdInfo info_autostart[] = {
720
    {"help", N_("autostart a domain")},
721
    {"desc",
722
     N_("Configure a domain to be automatically started at boot.")},
723 724 725
    {NULL, NULL}
};

726
static const vshCmdOptDef opts_autostart[] = {
727 728
    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
729 730 731
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
732
static bool
733
cmdAutostart(vshControl *ctl, const vshCmd *cmd)
734 735
{
    virDomainPtr dom;
736
    const char *name;
737 738
    int autostart;

739
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
740
        return false;
741

J
Jim Meyering 已提交
742
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
743
        return false;
744 745 746 747

    autostart = !vshCommandOptBool(cmd, "disable");

    if (virDomainSetAutostart(dom, autostart) < 0) {
748
        if (autostart)
749
            vshError(ctl, _("Failed to mark domain %s as autostarted"), name);
750
        else
751
            vshError(ctl, _("Failed to unmark domain %s as autostarted"), name);
752
        virDomainFree(dom);
E
Eric Blake 已提交
753
        return false;
754 755
    }

756
    if (autostart)
757
        vshPrint(ctl, _("Domain %s marked as autostarted\n"), name);
758
    else
759
        vshPrint(ctl, _("Domain %s unmarked as autostarted\n"), name);
760

761
    virDomainFree(dom);
E
Eric Blake 已提交
762
    return true;
763 764
}

K
Karel Zak 已提交
765
/*
766
 * "connect" command
K
Karel Zak 已提交
767
 */
768
static const vshCmdInfo info_connect[] = {
769
    {"help", N_("(re)connect to hypervisor")},
770
    {"desc",
771
     N_("Connect to local hypervisor. This is built-in command after shell start up.")},
772
    {NULL, NULL}
K
Karel Zak 已提交
773 774
};

775
static const vshCmdOptDef opts_connect[] = {
E
Eric Blake 已提交
776 777
    {"name",     VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
     N_("hypervisor connection URI")},
778
    {"readonly", VSH_OT_BOOL, 0, N_("read-only connection")},
779
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
780 781
};

E
Eric Blake 已提交
782
static bool
783
cmdConnect(vshControl *ctl, const vshCmd *cmd)
784
{
E
Eric Blake 已提交
785
    bool ro = vshCommandOptBool(cmd, "readonly");
786
    const char *name = NULL;
787

K
Karel Zak 已提交
788
    if (ctl->conn) {
789 790 791
        int ret;
        if ((ret = virConnectClose(ctl->conn)) != 0) {
            vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
E
Eric Blake 已提交
792
            return false;
K
Karel Zak 已提交
793 794 795
        }
        ctl->conn = NULL;
    }
796

797
    VIR_FREE(ctl->name);
798 799
    if (vshCommandOptString(cmd, "name", &name) < 0) {
        vshError(ctl, "%s", _("Please specify valid connection URI"));
E
Eric Blake 已提交
800
        return false;
801
    }
802
    ctl->name = vshStrdup(ctl, name);
K
Karel Zak 已提交
803

804
    ctl->useGetInfo = false;
805
    ctl->useSnapshotOld = false;
E
Eric Blake 已提交
806
    ctl->readonly = ro;
K
Karel Zak 已提交
807

808 809 810
    ctl->conn = virConnectOpenAuth(ctl->name, virConnectAuthPtrDefault,
                                   ctl->readonly ? VIR_CONNECT_RO : 0);

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

E
Eric Blake 已提交
814
    return !!ctl->conn;
K
Karel Zak 已提交
815 816
}

817 818
#ifndef WIN32

819
/*
820
 * "console" command
821
 */
822
static const vshCmdInfo info_console[] = {
823
    {"help", N_("connect to the guest console")},
824
    {"desc",
825
     N_("Connect the virtual serial console for the guest")},
826 827 828
    {NULL, NULL}
};

829
static const vshCmdOptDef opts_console[] = {
830
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
831
    {"devname", VSH_OT_STRING, 0, N_("character device name")},
832 833 834 835
    {"force", VSH_OT_BOOL, 0,
      N_("force console connection (disconnect already connected sessions)")},
    {"safe", VSH_OT_BOOL, 0,
      N_("only connect if safe console handling is supported")},
836 837 838
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
839
static bool
840 841 842
cmdRunConsole(vshControl *ctl, virDomainPtr dom,
              const char *name,
              unsigned int flags)
843
{
E
Eric Blake 已提交
844
    bool ret = false;
845
    int state;
846

847
    if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
848 849 850 851
        vshError(ctl, "%s", _("Unable to get domain status"));
        goto cleanup;
    }

852
    if (state == VIR_DOMAIN_SHUTOFF) {
853 854 855 856
        vshError(ctl, "%s", _("The domain is not running"));
        goto cleanup;
    }

857 858 859 860 861
    if (!isatty(STDIN_FILENO)) {
        vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
        goto cleanup;
    }

862
    vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
863
    vshPrintExtra(ctl, _("Escape character is %s\n"), ctl->escapeChar);
864
    fflush(stdout);
865
    if (vshRunConsole(dom, name, ctl->escapeChar, flags) == 0)
E
Eric Blake 已提交
866
        ret = true;
867 868

 cleanup:
869

870 871 872
    return ret;
}

E
Eric Blake 已提交
873
static bool
874 875 876
cmdConsole(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
877
    bool ret = false;
878 879 880
    bool force = vshCommandOptBool(cmd, "force");
    bool safe = vshCommandOptBool(cmd, "safe");
    unsigned int flags = 0;
881
    const char *name = NULL;
882

883
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
884
        return false;
885 886

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
887
        return false;
888

889 890 891 892
    if (vshCommandOptString(cmd, "devname", &name) < 0) {
        vshError(ctl, "%s", _("Invalid devname"));
        goto cleanup;
    }
893

894 895 896 897 898 899
    if (force)
        flags |= VIR_DOMAIN_CONSOLE_FORCE;
    if (safe)
        flags |= VIR_DOMAIN_CONSOLE_SAFE;

    ret = cmdRunConsole(ctl, dom, name, flags);
900

901
cleanup:
902 903 904 905
    virDomainFree(dom);
    return ret;
}

906 907 908
#endif /* WIN32 */


K
Karel Zak 已提交
909 910 911
/*
 * "list" command
 */
912
static const vshCmdInfo info_list[] = {
913 914
    {"help", N_("list domains")},
    {"desc", N_("Returns list of domains.")},
915
    {NULL, NULL}
K
Karel Zak 已提交
916 917
};

918
static const vshCmdOptDef opts_list[] = {
919 920
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive domains")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active domains")},
921 922 923 924 925
    {"transient", VSH_OT_BOOL, 0, N_("list transient domains")},
    {"persistent", VSH_OT_BOOL, 0, N_("list persistent domains")},
    {"uuid", VSH_OT_BOOL, 0, N_("list uuid's only")},
    {"name", VSH_OT_BOOL, 0, N_("list domain names only")},
    {"table", VSH_OT_BOOL, 0, N_("list table (default)")},
E
Eric Blake 已提交
926 927
    {"managed-save", VSH_OT_BOOL, 0,
     N_("mark domains with managed save state")},
928
    {"title", VSH_OT_BOOL, 0, N_("show short domain description")},
929 930 931
    {NULL, 0, 0, NULL}
};

K
Karel Zak 已提交
932

E
Eric Blake 已提交
933
static bool
934
cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
935
{
936 937 938 939
    int inactive = vshCommandOptBool(cmd, "inactive");
    int all = vshCommandOptBool(cmd, "all");
    int active = !inactive || all ? 1 : 0;
    int *ids = NULL, maxid = 0, i;
940
    char **names = NULL;
941
    int maxname = 0;
E
Eric Blake 已提交
942
    bool managed = vshCommandOptBool(cmd, "managed-save");
943 944 945 946 947 948 949 950
    bool optTitle = vshCommandOptBool(cmd, "title");
    bool optTable = vshCommandOptBool(cmd, "table");
    bool optUUID = vshCommandOptBool(cmd, "uuid");
    bool optName = vshCommandOptBool(cmd, "name");
    bool optPersistent = vshCommandOptBool(cmd, "persistent");
    bool optTransient = vshCommandOptBool(cmd, "transient");
    bool persistUsed = true;
    virDomainPtr dom = NULL;
951
    char *title;
952
    char uuid[VIR_UUID_STRING_BUFLEN];
E
Eric Blake 已提交
953
    int state;
954
    int persistent;
955
    bool ret = false;
E
Eric Blake 已提交
956

957
    inactive |= all;
K
Karel Zak 已提交
958

959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
    /* process arguments */
    if (!optPersistent && !optTransient) {
        optPersistent = true;
        optTransient = true;
        persistUsed = false;
    }

    if (optTable + optName + optUUID > 1) {
        vshError(ctl, "%s",
                 _("Only one argument from --table, --name and --uuid"
                   "may be specified."));
        return false;
    }

    if (!optUUID && !optName)
        optTable = true;

976
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
977
        return false;
978

979
    if (active) {
980 981
        maxid = virConnectNumOfDomains(ctl->conn);
        if (maxid < 0) {
982
            vshError(ctl, "%s", _("Failed to list active domains"));
E
Eric Blake 已提交
983
            return false;
984 985 986 987
        }
        if (maxid) {
            ids = vshMalloc(ctl, sizeof(int) * maxid);

988
            if ((maxid = virConnectListDomains(ctl->conn, ids, maxid)) < 0) {
989
                vshError(ctl, "%s", _("Failed to list active domains"));
990
                goto cleanup;
991 992
            }

993
            qsort(ids, maxid, sizeof(int), idsorter);
994
        }
995
    }
996

997
    if (inactive) {
998 999
        maxname = virConnectNumOfDefinedDomains(ctl->conn);
        if (maxname < 0) {
1000
            vshError(ctl, "%s", _("Failed to list inactive domains"));
1001
            goto cleanup;
1002
        }
1003 1004 1005
        if (maxname) {
            names = vshMalloc(ctl, sizeof(char *) * maxname);

1006 1007 1008
            if ((maxname = virConnectListDefinedDomains(ctl->conn,
                                                        names,
                                                        maxname)) < 0) {
1009
                vshError(ctl, "%s", _("Failed to list inactive domains"));
1010
                goto cleanup;
1011
            }
1012

1013
            qsort(&names[0], maxname, sizeof(char*), namesorter);
1014
        }
1015
    }
1016

1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
    /* print table header in legacy mode */
    if (optTable) {
        if (optTitle) {
            vshPrintExtra(ctl, " %-5s %-30s %-10s %-20s\n%s\n",
                          _("Id"), _("Name"), _("State"), _("Title"),
                          "-----------------------------------------"
                          "-----------------------------------------");
        } else {
            vshPrintExtra(ctl, " %-5s %-30s %s\n%s\n",
                          _("Id"), _("Name"), _("State"),
                          "-----------------------------------------"
                          "-----------");
        }
1030
    }
1031 1032

    for (i = 0; i < maxid; i++) {
1033
         dom = virDomainLookupByID(ctl->conn, ids[i]);
1034 1035

        /* this kind of work with domains is not atomic operation */
K
Karel Zak 已提交
1036 1037
        if (!dom)
            continue;
1038

1039 1040 1041 1042 1043
        if (persistUsed) {
            persistent = virDomainIsPersistent(dom);
            if (persistent < 0) {
                vshError(ctl, "%s",
                         _("Failed to determine domain's persistent state"));
1044
                goto cleanup;
1045
            }
1046

1047 1048 1049 1050 1051 1052
            if ((persistent && !optPersistent) ||
                (!persistent && !optTransient)) {
                virDomainFree(dom);
                dom = NULL;
                continue;
            }
1053
       }
1054

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
       if (optTable) {
           if (optTitle) {
               if (!(title = vshGetDomainDescription(ctl, dom, true, 0)))
                   goto cleanup;

               vshPrint(ctl, " %-5d %-30s %-10s %-20s\n",
                        virDomainGetID(dom),
                        virDomainGetName(dom),
                        _(vshDomainStateToString(vshDomainState(ctl, dom, NULL))),
                        title);
               VIR_FREE(title);
           } else {
               vshPrint(ctl, " %-5d %-30s %s\n",
                        virDomainGetID(dom),
                        virDomainGetName(dom),
                        _(vshDomainStateToString(vshDomainState(ctl, dom, NULL))));
           }
       } else if (optUUID) {
           if (virDomainGetUUIDString(dom, uuid) < 0) {
               vshError(ctl, "%s",
                        _("Failed to get domain's UUID"));
               goto cleanup;
           }
           vshPrint(ctl, "%s\n", uuid);
       } else if (optName) {
           vshPrint(ctl, "%s\n", virDomainGetName(dom));
       }
E
Eric Blake 已提交
1082

1083 1084 1085
       virDomainFree(dom);
       dom = NULL;
    }
1086

1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
    if (optPersistent) {
        for (i = 0; i < maxname; i++) {
            dom = virDomainLookupByName(ctl->conn, names[i]);

            /* this kind of work with domains is not atomic operation */
            if (!dom)
                continue;

            if (optTable) {
                state = vshDomainState(ctl, dom, NULL);
                if (managed && state == VIR_DOMAIN_SHUTOFF &&
                    virDomainHasManagedSaveImage(dom, 0) > 0)
                    state = -2;

                if (optTitle) {
                    if (!(title = vshGetDomainDescription(ctl, dom, true, 0)))
                        goto cleanup;

                    vshPrint(ctl, " %-5s %-30s %-10s %-20s\n",
                             "-",
                             names[i],
                             state == -2 ? _("saved") : _(vshDomainStateToString(state)),
                             title);
                    VIR_FREE(title);
                } else {
                    vshPrint(ctl, " %-5s %-30s %s\n",
                             "-",
                             names[i],
                             state == -2 ? _("saved") : _(vshDomainStateToString(state)));
                }
           } else if (optUUID) {
               if (virDomainGetUUIDString(dom, uuid) < 0) {
                   vshError(ctl, "%s",
                            _("Failed to get domain's UUID"));
                   goto cleanup;
               }
               vshPrint(ctl, "%s\n", uuid);
           } else if (optName) {
               vshPrint(ctl, "%s\n", names[i]);
           }

           virDomainFree(dom);
           dom = NULL;
1130
        }
1131
    }
1132 1133 1134

    ret = true;
cleanup:
1135 1136
    if (dom)
        virDomainFree(dom);
1137
    VIR_FREE(ids);
1138 1139
    for (i = 0; i < maxname; i++)
        VIR_FREE(names[i]);
1140
    VIR_FREE(names);
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
    return ret;
}

/*
 * "desc" command for managing domain description and title
 */
static const vshCmdInfo info_desc[] = {
    {"help", N_("show or set domain's description or title")},
    {"desc", N_("Allows to show or modify description or title of a domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_desc[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"live", VSH_OT_BOOL, 0, N_("modify/get running state")},
    {"config", VSH_OT_BOOL, 0, N_("modify/get persistent configuration")},
    {"current", VSH_OT_BOOL, 0, N_("modify/get current state configuration")},
    {"title", VSH_OT_BOOL, 0, N_("modify the title instead of description")},
    {"edit", VSH_OT_BOOL, 0, N_("open an editor to modify the description")},
    {"new-desc", VSH_OT_ARGV, 0, N_("message")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDesc(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    virDomainPtr dom;
    bool config = vshCommandOptBool(cmd, "config");
    bool live = vshCommandOptBool(cmd, "live");
1170
    bool current = vshCommandOptBool(cmd, "current");
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186

    bool title = vshCommandOptBool(cmd, "title");
    bool edit = vshCommandOptBool(cmd, "edit");

    int state;
    int type;
    char *desc = NULL;
    char *desc_edited = NULL;
    char *tmp = NULL;
    char *tmpstr;
    const vshCmdOpt *opt = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    bool pad = false;
    bool ret = false;
    unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;

1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
    }

1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

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

    if ((state = vshDomainState(ctl, dom, NULL)) < 0)
        goto cleanup;

    while ((opt = vshCommandOptArgv(cmd, opt))) {
        if (pad)
            virBufferAddChar(&buf, ' ');
        pad = true;
        virBufferAdd(&buf, opt->data, -1);
    }

    if (title)
        type = VIR_DOMAIN_METADATA_TITLE;
    else
        type = VIR_DOMAIN_METADATA_DESCRIPTION;

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to collect new description/title"));
        goto cleanup;
    }
    desc = virBufferContentAndReset(&buf);

    if (edit || desc) {
        if (!desc) {
                desc = vshGetDomainDescription(ctl, dom, title,
                                           config?VIR_DOMAIN_XML_INACTIVE:0);
                if (!desc)
                    goto cleanup;
        }

        if (edit) {
            /* Create and open the temporary file. */
            if (!(tmp = editWriteToTempFile(ctl, desc)))
                goto cleanup;

            /* Start the editor. */
            if (editFile(ctl, tmp) == -1)
                goto cleanup;

            /* Read back the edited file. */
            if (!(desc_edited = editReadBackFile(ctl, tmp)))
                goto cleanup;

            /* strip a possible newline at the end of file; some
             * editors enforce a newline, this makes editing the title
1250
             * more convenient */
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
            if (title &&
                (tmpstr = strrchr(desc_edited, '\n')) &&
                *(tmpstr+1) == '\0')
                *tmpstr = '\0';

            /* Compare original XML with edited.  Has it changed at all? */
            if (STREQ(desc, desc_edited)) {
                vshPrint(ctl, _("Domain description not changed.\n"));
                ret = true;
                goto cleanup;
            }

            VIR_FREE(desc);
            desc = desc_edited;
            desc_edited = NULL;
        }

        if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) {
            vshError(ctl, "%s",
                     _("Failed to set new domain description"));
            goto cleanup;
        }
        vshPrint(ctl, "%s", _("Domain description updated successfully"));
    } else {
        desc = vshGetDomainDescription(ctl, dom, title,
                                       config?VIR_DOMAIN_XML_INACTIVE:0);
        if (!desc)
            goto cleanup;

        if (strlen(desc) > 0)
            vshPrint(ctl, "%s", desc);
        else
            vshPrint(ctl, _("No description for domain: %s"),
                     virDomainGetName(dom));
    }

    ret = true;
cleanup:
    VIR_FREE(desc_edited);
    VIR_FREE(desc);
    if (tmp) {
        unlink(tmp);
        VIR_FREE(tmp);
    }
A
Alex Jia 已提交
1295 1296
    if (dom)
        virDomainFree(dom);
1297
    return ret;
K
Karel Zak 已提交
1298 1299 1300
}

/*
K
Karel Zak 已提交
1301
 * "domstate" command
K
Karel Zak 已提交
1302
 */
1303
static const vshCmdInfo info_domstate[] = {
1304 1305
    {"help", N_("domain state")},
    {"desc", N_("Returns state about a domain.")},
1306
    {NULL, NULL}
K
Karel Zak 已提交
1307 1308
};

1309
static const vshCmdOptDef opts_domstate[] = {
1310
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
1311
    {"reason", VSH_OT_BOOL, 0, N_("also print reason for the state")},
1312
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
1313 1314
};

E
Eric Blake 已提交
1315
static bool
1316
cmdDomstate(vshControl *ctl, const vshCmd *cmd)
1317
{
K
Karel Zak 已提交
1318
    virDomainPtr dom;
E
Eric Blake 已提交
1319
    bool ret = true;
1320 1321
    int showReason = vshCommandOptBool(cmd, "reason");
    int state, reason;
1322

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

J
Jim Meyering 已提交
1326
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
1327
        return false;
1328

1329
    if ((state = vshDomainState(ctl, dom, &reason)) < 0) {
E
Eric Blake 已提交
1330
        ret = false;
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
        goto cleanup;
    }

    if (showReason) {
        vshPrint(ctl, "%s (%s)\n",
                 _(vshDomainStateToString(state)),
                 vshDomainStateReasonToString(state, reason));
    } else {
        vshPrint(ctl, "%s\n",
                 _(vshDomainStateToString(state)));
    }
1342

1343
cleanup:
1344 1345 1346 1347
    virDomainFree(dom);
    return ret;
}

1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394
/*
 * "domcontrol" command
 */
static const vshCmdInfo info_domcontrol[] = {
    {"help", N_("domain control interface state")},
    {"desc", N_("Returns state of a control interface to the domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_domcontrol[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomControl(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    bool ret = true;
    virDomainControlInfo info;

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

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

    if (virDomainGetControlInfo(dom, &info, 0) < 0) {
        ret = false;
        goto cleanup;
    }

    if (info.state != VIR_DOMAIN_CONTROL_OK &&
        info.state != VIR_DOMAIN_CONTROL_ERROR) {
        vshPrint(ctl, "%s (%0.3fs)\n",
                 _(vshDomainControlStateToString(info.state)),
                 info.stateTime / 1000.0);
    } else {
        vshPrint(ctl, "%s\n",
                 _(vshDomainControlStateToString(info.state)));
    }

cleanup:
    virDomainFree(dom);
    return ret;
}

1395 1396
/* "domblkstat" command
 */
1397
static const vshCmdInfo info_domblkstat[] = {
1398
    {"help", N_("get device block stats for a domain")},
1399 1400
    {"desc", N_("Get device block stats for a running domain. See man page or "
                "use --human for explanation of fields")},
1401 1402 1403
    {NULL,NULL}
};

1404
static const vshCmdOptDef opts_domblkstat[] = {
1405 1406
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420
    {"human",  VSH_OT_BOOL, 0, N_("print a more human readable output")},
    {NULL, 0, 0, NULL}
};

struct _domblkstat_sequence {
    const char *field;  /* field name */
    const char *legacy; /* legacy name from previous releases */
    const char *human;  /* human-friendly explanation */
};

/* sequence of values for output to honor legacy format from previous
 * versions */
static const struct _domblkstat_sequence domblkstat_output[] = {
    { VIR_DOMAIN_BLOCK_STATS_READ_REQ,          "rd_req",
1421
      N_("number of read operations:") }, /* 0 */
1422
    { VIR_DOMAIN_BLOCK_STATS_READ_BYTES,        "rd_bytes",
1423
      N_("number of bytes read:") }, /* 1 */
1424
    { VIR_DOMAIN_BLOCK_STATS_WRITE_REQ,         "wr_req",
1425
      N_("number of write operations:") }, /* 2 */
1426
    { VIR_DOMAIN_BLOCK_STATS_WRITE_BYTES,       "wr_bytes",
1427
      N_("number of bytes written:") }, /* 3 */
1428
    { VIR_DOMAIN_BLOCK_STATS_ERRS,              "errs",
1429
      N_("error count:") }, /* 4 */
1430
    { VIR_DOMAIN_BLOCK_STATS_FLUSH_REQ,         NULL,
1431
      N_("number of flush operations:") }, /* 5 */
1432
    { VIR_DOMAIN_BLOCK_STATS_READ_TOTAL_TIMES,  NULL,
1433
      N_("total duration of reads (ns):") }, /* 6 */
1434
    { VIR_DOMAIN_BLOCK_STATS_WRITE_TOTAL_TIMES, NULL,
1435
      N_("total duration of writes (ns):") }, /* 7 */
1436 1437 1438 1439 1440 1441 1442
    { VIR_DOMAIN_BLOCK_STATS_FLUSH_TOTAL_TIMES, NULL,
      N_("total duration of flushes (ns):") }, /* 8 */
    { NULL, NULL, NULL }
};

#define DOMBLKSTAT_LEGACY_PRINT(ID, VALUE)              \
    if (VALUE >= 0)                                     \
1443 1444
        vshPrint(ctl, "%s %-*s %lld\n", device,         \
                 human ? 31 : 0,                        \
1445 1446 1447
                 human ? _(domblkstat_output[ID].human) \
                 : domblkstat_output[ID].legacy,        \
                 VALUE);
1448

E
Eric Blake 已提交
1449
static bool
1450
cmdDomblkstat (vshControl *ctl, const vshCmd *cmd)
1451 1452
{
    virDomainPtr dom;
1453
    const char *name = NULL, *device = NULL;
1454
    struct _virDomainBlockStats stats;
1455
    virTypedParameterPtr params = NULL;
1456 1457 1458
    virTypedParameterPtr par = NULL;
    char *value = NULL;
    const char *field = NULL;
1459
    int rc, nparams = 0;
1460
    int i = 0;
1461
    bool ret = false;
1462
    bool human = vshCommandOptBool(cmd, "human"); /* human readable output */
1463

1464
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1465
        return false;
1466

1467
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
1468
        return false;
1469

1470
    if (vshCommandOptString(cmd, "device", &device) <= 0)
1471
        goto cleanup;
1472

1473 1474 1475 1476 1477 1478 1479
    rc = virDomainBlockStatsFlags(dom, device, NULL, &nparams, 0);

    /* It might fail when virDomainBlockStatsFlags is not
     * supported on older libvirt, fallback to use virDomainBlockStats
     * then.
     */
    if (rc < 0) {
1480 1481
        /* try older API if newer is not supported */
        if (last_error->code != VIR_ERR_NO_SUPPORT)
1482 1483
            goto cleanup;

1484 1485
        virFreeError(last_error);
        last_error = NULL;
1486

1487 1488 1489 1490 1491 1492
        if (virDomainBlockStats(dom, device, &stats,
                                sizeof stats) == -1) {
            vshError(ctl, _("Failed to get block stats %s %s"),
                     name, device);
            goto cleanup;
        }
1493

1494 1495 1496 1497
        /* human friendly output */
        if (human) {
            vshPrint(ctl, N_("Device: %s\n"), device);
            device = "";
1498
        }
1499 1500 1501 1502 1503 1504

        DOMBLKSTAT_LEGACY_PRINT(0, stats.rd_req);
        DOMBLKSTAT_LEGACY_PRINT(1, stats.rd_bytes);
        DOMBLKSTAT_LEGACY_PRINT(2, stats.wr_req);
        DOMBLKSTAT_LEGACY_PRINT(3, stats.wr_bytes);
        DOMBLKSTAT_LEGACY_PRINT(4, stats.errs);
1505
    } else {
1506
        params = vshCalloc(ctl, nparams, sizeof(*params));
1507

1508 1509 1510 1511
        if (virDomainBlockStatsFlags (dom, device, params, &nparams, 0) < 0) {
            vshError(ctl, _("Failed to get block stats %s %s"), name, device);
            goto cleanup;
        }
1512

1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
        /* set for prettier output */
        if (human) {
            vshPrint(ctl, N_("Device: %s\n"), device);
            device = "";
        }

        /* at first print all known values in desired order */
        for (i = 0; domblkstat_output[i].field != NULL; i++) {
            if (!(par = vshFindTypedParamByName(domblkstat_output[i].field,
                                                params,
                                                nparams)))
                continue;

1526
            value = vshGetTypedParamValue(ctl, par);
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541

            /* to print other not supported fields, mark the already printed */
            par->field[0] = '\0'; /* set the name to empty string */

            /* translate into human readable or legacy spelling */
            field = NULL;
            if (human)
                field = _(domblkstat_output[i].human);
            else
                field = domblkstat_output[i].legacy;

            /* use the provided spelling if no translation is available */
            if (!field)
                field = domblkstat_output[i].field;

1542 1543
            vshPrint(ctl, "%s %-*s %s\n", device,
                     human ? 31 : 0, field, value);
1544 1545 1546 1547 1548

            VIR_FREE(value);
        }

        /* go through the fields again, for remaining fields */
1549
        for (i = 0; i < nparams; i++) {
1550 1551
            if (!*params[i].field)
                continue;
1552

1553
            value = vshGetTypedParamValue(ctl, params+i);
1554 1555
            vshPrint(ctl, "%s %s %s\n", device, params[i].field, value);
            VIR_FREE(value);
1556 1557 1558 1559 1560 1561 1562
        }
    }

    ret = true;

cleanup:
    VIR_FREE(params);
1563
    virDomainFree(dom);
1564
    return ret;
1565
}
1566
#undef DOMBLKSTAT_LEGACY_PRINT
1567 1568 1569

/* "domifstat" command
 */
1570
static const vshCmdInfo info_domifstat[] = {
1571 1572
    {"help", N_("get network interface stats for a domain")},
    {"desc", N_("Get network interface stats for a running domain.")},
1573 1574 1575
    {NULL,NULL}
};

1576
static const vshCmdOptDef opts_domifstat[] = {
1577 1578
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device")},
1579 1580 1581
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1582
static bool
1583
cmdDomIfstat (vshControl *ctl, const vshCmd *cmd)
1584 1585
{
    virDomainPtr dom;
1586
    const char *name = NULL, *device = NULL;
1587 1588
    struct _virDomainInterfaceStats stats;

1589
    if (!vshConnectionUsability (ctl, ctl->conn))
E
Eric Blake 已提交
1590
        return false;
1591

J
Jim Meyering 已提交
1592
    if (!(dom = vshCommandOptDomain (ctl, cmd, &name)))
E
Eric Blake 已提交
1593
        return false;
1594

1595
    if (vshCommandOptString (cmd, "interface", &device) <= 0) {
L
Laine Stump 已提交
1596
        virDomainFree(dom);
E
Eric Blake 已提交
1597
        return false;
L
Laine Stump 已提交
1598
    }
1599 1600

    if (virDomainInterfaceStats (dom, device, &stats, sizeof stats) == -1) {
1601
        vshError(ctl, _("Failed to get interface stats %s %s"), name, device);
1602
        virDomainFree(dom);
E
Eric Blake 已提交
1603
        return false;
1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630
    }

    if (stats.rx_bytes >= 0)
        vshPrint (ctl, "%s rx_bytes %lld\n", device, stats.rx_bytes);

    if (stats.rx_packets >= 0)
        vshPrint (ctl, "%s rx_packets %lld\n", device, stats.rx_packets);

    if (stats.rx_errs >= 0)
        vshPrint (ctl, "%s rx_errs %lld\n", device, stats.rx_errs);

    if (stats.rx_drop >= 0)
        vshPrint (ctl, "%s rx_drop %lld\n", device, stats.rx_drop);

    if (stats.tx_bytes >= 0)
        vshPrint (ctl, "%s tx_bytes %lld\n", device, stats.tx_bytes);

    if (stats.tx_packets >= 0)
        vshPrint (ctl, "%s tx_packets %lld\n", device, stats.tx_packets);

    if (stats.tx_errs >= 0)
        vshPrint (ctl, "%s tx_errs %lld\n", device, stats.tx_errs);

    if (stats.tx_drop >= 0)
        vshPrint (ctl, "%s tx_drop %lld\n", device, stats.tx_drop);

    virDomainFree(dom);
E
Eric Blake 已提交
1631
    return true;
1632 1633
}

1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
/* "domif-setlink" command
 */
static const vshCmdInfo info_domif_setlink[] = {
    {"help", N_("set link state of a virtual interface")},
    {"desc", N_("Set link state of a domain's virtual interface. This command wraps usage of update-device command.")},
    {NULL,NULL}
};

static const vshCmdOptDef opts_domif_setlink[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
    {"state", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new state of the device")},
1646 1647
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
1648 1649 1650 1651 1652 1653 1654
    {NULL, 0, 0, NULL}
};

static bool
cmdDomIfSetLink (vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
1655
    const char *iface;
1656
    const char *state;
1657
    const char *value;
1658
    const char *desc;
1659 1660 1661
    unsigned char macaddr[VIR_MAC_BUFLEN];
    const char *element;
    const char *attr;
1662
    bool config;
1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677
    bool ret = false;
    unsigned int flags = 0;
    int i;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
    xmlNodePtr cur = NULL;
    xmlBufferPtr xml_buf = NULL;

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

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

1678
    if (vshCommandOptString(cmd, "interface", &iface) <= 0)
1679 1680 1681 1682 1683
        goto cleanup;

    if (vshCommandOptString(cmd, "state", &state) <= 0)
        goto cleanup;

1684
    config = vshCommandOptBool(cmd, "config");
1685 1686 1687 1688 1689 1690 1691

    if (STRNEQ(state, "up") && STRNEQ(state, "down")) {
        vshError(ctl, _("invalid link state '%s'"), state);
        goto cleanup;
    }

    /* get persistent or live description of network device */
1692
    desc = virDomainGetXMLDesc(dom, config ? VIR_DOMAIN_XML_INACTIVE : 0);
1693 1694 1695 1696 1697
    if (desc == NULL) {
        vshError(ctl, _("Failed to get domain description xml"));
        goto cleanup;
    }

1698
    if (config)
1699 1700 1701 1702 1703 1704 1705 1706
        flags = VIR_DOMAIN_AFFECT_CONFIG;
    else
        flags = VIR_DOMAIN_AFFECT_LIVE;

    if (virDomainIsActive(dom) == 0)
        flags = VIR_DOMAIN_AFFECT_CONFIG;

    /* extract current network device description */
1707
    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720
    VIR_FREE(desc);
    if (!xml) {
        vshError(ctl, _("Failed to parse domain description xml"));
        goto cleanup;
    }

    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
    if (obj == NULL || obj->type != XPATH_NODESET ||
        obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
        vshError(ctl, _("Failed to extract interface information or no interfaces found"));
        goto cleanup;
    }

1721
    if (virMacAddrParse(iface, macaddr) == 0) {
1722 1723 1724 1725 1726 1727 1728
        element = "mac";
        attr = "address";
    } else {
        element = "target";
        attr = "dev";
    }

1729 1730 1731 1732 1733 1734
    /* find interface with matching mac addr */
    for (i = 0; i < obj->nodesetval->nodeNr; i++) {
        cur = obj->nodesetval->nodeTab[i]->children;

        while (cur) {
            if (cur->type == XML_ELEMENT_NODE &&
1735 1736
                xmlStrEqual(cur->name, BAD_CAST element)) {
                value = virXMLPropString(cur, attr);
1737

1738 1739
                if (STRCASEEQ(value, iface)) {
                    VIR_FREE(value);
1740 1741
                    goto hit;
                }
1742
                VIR_FREE(value);
1743 1744 1745 1746 1747
            }
            cur = cur->next;
        }
    }

1748
    vshError(ctl, _("interface (%s: %s) not found"), element, iface);
1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821
    goto cleanup;

hit:
    /* find and modify/add link state node */
    /* try to find <link> element */
    cur = obj->nodesetval->nodeTab[i]->children;

    while (cur) {
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "link")) {
            /* found, just modify the property */
            xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);

            break;
        }
        cur = cur->next;
    }

    if (!cur) {
        /* element <link> not found, add one */
        cur = xmlNewChild(obj->nodesetval->nodeTab[i],
                          NULL,
                          BAD_CAST "link",
                          NULL);
        if (!cur)
            goto cleanup;

        if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
            goto cleanup;
    }

    xml_buf = xmlBufferCreate();
    if (!xml_buf) {
        vshError(ctl, _("Failed to allocate memory"));
        goto cleanup;
    }

    if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0 ) {
        vshError(ctl, _("Failed to create XML"));
        goto cleanup;
    }

    if (virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags) < 0) {
        vshError(ctl, _("Failed to update interface link state"));
        goto cleanup;
    } else {
        vshPrint(ctl, "%s", _("Device updated successfully\n"));
        ret = true;
    }

cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);

    if (dom)
        virDomainFree(dom);

    return ret;
}

/* "domif-getlink" command
 */
static const vshCmdInfo info_domif_getlink[] = {
    {"help", N_("get link state of a virtual interface")},
    {"desc", N_("Get link state of a domain's virtual interface.")},
    {NULL,NULL}
};

static const vshCmdOptDef opts_domif_getlink[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
1822 1823
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("Get persistent interface state")},
1824 1825 1826 1827 1828 1829 1830
    {NULL, 0, 0, NULL}
};

static bool
cmdDomIfGetLink (vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
1831
    const char *iface = NULL;
1832 1833
    int flags = 0;
    char *state = NULL;
1834 1835 1836 1837
    char *value = NULL;
    unsigned char macaddr[VIR_MAC_BUFLEN];
    const char *element;
    const char *attr;
1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851
    bool ret = false;
    int i;
    char *desc;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr cur = NULL;
    xmlXPathObjectPtr obj = NULL;

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

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

1852
    if (vshCommandOptString (cmd, "interface", &iface) <= 0) {
1853 1854 1855 1856
        virDomainFree(dom);
        return false;
    }

1857
    if (vshCommandOptBool(cmd, "config"))
1858 1859 1860 1861 1862 1863 1864 1865
        flags = VIR_DOMAIN_XML_INACTIVE;

    desc = virDomainGetXMLDesc(dom, flags);
    if (desc == NULL) {
        vshError(ctl, _("Failed to get domain description xml"));
        goto cleanup;
    }

1866
    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879
    VIR_FREE(desc);
    if (!xml) {
        vshError(ctl, _("Failed to parse domain description xml"));
        goto cleanup;
    }

    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
    if (obj == NULL || obj->type != XPATH_NODESET ||
        obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
        vshError(ctl, _("Failed to extract interface information or no interfaces found"));
        goto cleanup;
    }

1880
    if (virMacAddrParse(iface, macaddr) == 0) {
1881 1882 1883 1884 1885 1886 1887
        element = "mac";
        attr = "address";
    } else {
        element = "target";
        attr = "dev";
    }

1888 1889 1890 1891 1892 1893
    /* find interface with matching mac addr */
    for (i = 0; i < obj->nodesetval->nodeNr; i++) {
        cur = obj->nodesetval->nodeTab[i]->children;

        while (cur) {
            if (cur->type == XML_ELEMENT_NODE &&
1894
                xmlStrEqual(cur->name, BAD_CAST element)) {
1895

1896
                value = virXMLPropString(cur, attr);
1897

1898 1899
                if (STRCASEEQ(value, iface)) {
                    VIR_FREE(value);
1900 1901
                    goto hit;
                }
1902
                VIR_FREE(value);
1903 1904 1905 1906 1907
            }
            cur = cur->next;
        }
    }

1908
    vshError(ctl, _("Interface (%s: %s) not found."), element, iface);
1909 1910 1911 1912 1913 1914 1915 1916 1917
    goto cleanup;

hit:
    cur = obj->nodesetval->nodeTab[i]->children;
    while (cur) {
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "link")) {

            state = virXMLPropString(cur, "state");
1918
            vshPrint(ctl, "%s %s", iface, state);
1919 1920 1921 1922 1923 1924 1925 1926
            VIR_FREE(state);

            goto cleanup;
        }
        cur = cur->next;
    }

    /* attribute not found */
1927
    vshPrint(ctl, "%s default", iface);
1928

1929
    ret = true;
1930 1931 1932 1933 1934 1935 1936 1937 1938 1939
cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
    if (dom)
        virDomainFree(dom);

    return ret;
}

1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134
/* "domiftune" command
 */
static const vshCmdInfo info_domiftune[] = {
    {"help", N_("get/set parameters of a virtual interface")},
    {"desc", N_("Get/set parameters of a domain's virtual interface.")},
    {NULL,NULL}
};

static const vshCmdOptDef opts_domiftune[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device")},
    {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")},
    {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")},
    {"config", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect next boot")},
    {"live", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect running domain")},
    {"current", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect current domain")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    const char *name = NULL, *device = NULL,
               *inboundStr = NULL, *outboundStr = NULL;
    unsigned int flags = 0;
    int nparams = 0;
    virTypedParameterPtr params = NULL;
    bool ret = false;
    int current = vshCommandOptBool(cmd, "current");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    virNetDevBandwidthRate inbound, outbound;
    int i;

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
    }

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

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    if (vshCommandOptString(cmd, "interface", &device) <= 0) {
        virDomainFree(dom);
        return false;
    }

    if (vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
        vshError(ctl, "missing argument");
        goto cleanup;
    }

    memset(&inbound, 0, sizeof(inbound));
    memset(&outbound, 0, sizeof(outbound));

    if (inboundStr) {
        if (parseRateStr(inboundStr, &inbound) < 0) {
            vshError(ctl, _("inbound format is incorrect"));
            goto cleanup;
        }
        if (inbound.average == 0) {
            vshError(ctl, _("inbound average is mandatory"));
            goto cleanup;
        }
        nparams++; /* average */
        if (inbound.peak) nparams++;
        if (inbound.burst) nparams++;
    }
    if (outboundStr) {
        if (parseRateStr(outboundStr, &outbound) < 0) {
            vshError(ctl, _("outbound format is incorrect"));
            goto cleanup;
        }
        if (outbound.average == 0) {
            vshError(ctl, _("outbound average is mandatory"));
            goto cleanup;
        }
        nparams++; /* average */
        if (outbound.peak) nparams++;
        if (outbound.burst) nparams++;
    }

    if (nparams == 0) {
        /* get the number of interface parameters */
        if (virDomainGetInterfaceParameters(dom, device, NULL, &nparams, flags) != 0) {
            vshError(ctl, "%s",
                     _("Unable to get number of interface parameters"));
            goto cleanup;
        }

        if (nparams == 0) {
            /* nothing to output */
            ret = true;
            goto cleanup;
        }

        /* get all interface parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));
        if (!params) {
            virReportOOMError();
            goto cleanup;
        }
        if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) != 0) {
            vshError(ctl, "%s", _("Unable to get interface parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
            char *str = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
            VIR_FREE(str);
        }
    } else {
        /* set the interface parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));
        if (!params) {
            virReportOOMError();
            goto cleanup;
        }

        for (i = 0; i < nparams; i++)
            params[i].type = VIR_TYPED_PARAM_UINT;

        i = 0;
        if (inbound.average && i < nparams) {
            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
                           sizeof(params[i].field)))
                goto cleanup;
            params[i].value.ui = inbound.average;
            i++;
        }
        if (inbound.peak && i < nparams) {
            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_PEAK,
                           sizeof(params[i].field)))
                goto cleanup;
            params[i].value.ui = inbound.peak;
            i++;
        }
        if (inbound.burst && i < nparams) {
            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_BURST,
                           sizeof(params[i].field)))
                goto cleanup;
            params[i].value.ui = inbound.burst;
            i++;
        }
        if (outbound.average && i < nparams) {
            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
                           sizeof(params[i].field)))
                goto cleanup;
            params[i].value.ui = outbound.average;
            i++;
        }
        if (outbound.peak && i < nparams) {
            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
                           sizeof(params[i].field)))
                goto cleanup;
            params[i].value.ui = outbound.peak;
            i++;
        }
        if (outbound.burst && i < nparams) {
            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_BURST,
                           sizeof(params[i].field)))
                goto cleanup;
            params[i].value.ui = outbound.burst;
            i++;
        }

        if (virDomainSetInterfaceParameters(dom, device, params, nparams, flags) != 0) {
            vshError(ctl, "%s", _("Unable to set interface parameters"));
            goto cleanup;
        }
    }

    ret = true;

cleanup:
    virTypedParameterArrayClear(params, nparams);
    VIR_FREE(params);
    virDomainFree(dom);
    return ret;
}

2135 2136 2137
/*
 * "dommemstats" command
 */
2138
static const vshCmdInfo info_dommemstat[] = {
2139
    {"help", N_("get memory statistics for a domain")},
Y
Yuri Chornoivan 已提交
2140
    {"desc", N_("Get memory statistics for a running domain.")},
2141 2142 2143
    {NULL,NULL}
};

2144
static const vshCmdOptDef opts_dommemstat[] = {
2145
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
2146 2147 2148
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2149
static bool
2150
cmdDomMemStat(vshControl *ctl, const vshCmd *cmd)
2151 2152
{
    virDomainPtr dom;
2153
    const char *name;
2154 2155 2156
    struct _virDomainMemoryStat stats[VIR_DOMAIN_MEMORY_STAT_NR];
    unsigned int nr_stats, i;

2157
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2158
        return false;
2159 2160

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2161
        return false;
2162 2163 2164 2165 2166

    nr_stats = virDomainMemoryStats (dom, stats, VIR_DOMAIN_MEMORY_STAT_NR, 0);
    if (nr_stats == -1) {
        vshError(ctl, _("Failed to get memory statistics for domain %s"), name);
        virDomainFree(dom);
E
Eric Blake 已提交
2167
        return false;
2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182
    }

    for (i = 0; i < nr_stats; i++) {
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_IN)
            vshPrint (ctl, "swap_in %llu\n", stats[i].val);
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_OUT)
            vshPrint (ctl, "swap_out %llu\n", stats[i].val);
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT)
            vshPrint (ctl, "major_fault %llu\n", stats[i].val);
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT)
            vshPrint (ctl, "minor_fault %llu\n", stats[i].val);
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_UNUSED)
            vshPrint (ctl, "unused %llu\n", stats[i].val);
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_AVAILABLE)
            vshPrint (ctl, "available %llu\n", stats[i].val);
2183 2184
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON)
            vshPrint (ctl, "actual %llu\n", stats[i].val);
M
Martin Kletzander 已提交
2185 2186
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_RSS)
            vshPrint (ctl, "rss %llu\n", stats[i].val);
2187 2188 2189
    }

    virDomainFree(dom);
E
Eric Blake 已提交
2190
    return true;
2191 2192
}

2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207
/*
 * "domblkinfo" command
 */
static const vshCmdInfo info_domblkinfo[] = {
    {"help", N_("domain block device size information")},
    {"desc", N_("Get block device size info for a domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_domblkinfo[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2208
static bool
2209 2210 2211 2212
cmdDomblkinfo(vshControl *ctl, const vshCmd *cmd)
{
    virDomainBlockInfo info;
    virDomainPtr dom;
E
Eric Blake 已提交
2213
    bool ret = true;
2214
    const char *device = NULL;
2215

2216
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2217
        return false;
2218 2219

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
2220
        return false;
2221

2222
    if (vshCommandOptString (cmd, "device", &device) <= 0) {
2223
        virDomainFree(dom);
E
Eric Blake 已提交
2224
        return false;
2225 2226 2227 2228
    }

    if (virDomainGetBlockInfo(dom, device, &info, 0) < 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
2229
        return false;
2230 2231 2232 2233 2234 2235 2236 2237 2238 2239
    }

    vshPrint(ctl, "%-15s %llu\n", _("Capacity:"), info.capacity);
    vshPrint(ctl, "%-15s %llu\n", _("Allocation:"), info.allocation);
    vshPrint(ctl, "%-15s %llu\n", _("Physical:"), info.physical);

    virDomainFree(dom);
    return ret;
}

2240 2241 2242 2243 2244
/*
 * "domblklist" command
 */
static const vshCmdInfo info_domblklist[] = {
    {"help", N_("list all domain blocks")},
2245
    {"desc", N_("Get the summary of block devices for a domain.")},
2246 2247 2248 2249 2250 2251 2252
    {NULL, NULL}
};

static const vshCmdOptDef opts_domblklist[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"inactive", VSH_OT_BOOL, 0,
     N_("get inactive rather than running configuration")},
2253 2254
    {"details", VSH_OT_BOOL, 0,
     N_("additionally display the type and device value")},
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269
    {NULL, 0, 0, NULL}
};

static bool
cmdDomblklist(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    bool ret = false;
    unsigned int flags = 0;
    char *xml = NULL;
    xmlDocPtr xmldoc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    int ndisks;
    xmlNodePtr *disks = NULL;
    int i;
2270
    bool details = false;
2271 2272 2273 2274

    if (vshCommandOptBool(cmd, "inactive"))
        flags |= VIR_DOMAIN_XML_INACTIVE;

2275 2276
    details = vshCommandOptBool(cmd, "details");

2277 2278 2279 2280 2281 2282 2283 2284 2285 2286
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

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

    xml = virDomainGetXMLDesc(dom, flags);
    if (!xml)
        goto cleanup;

2287
    xmldoc = virXMLParseStringCtxt(xml, _("(domain_definition)"), &ctxt);
2288 2289 2290 2291 2292 2293 2294
    if (!xmldoc)
        goto cleanup;

    ndisks = virXPathNodeSet("./devices/disk", ctxt, &disks);
    if (ndisks < 0)
        goto cleanup;

2295 2296 2297 2298 2299 2300
    if (details)
        vshPrint(ctl, "%-10s %-10s %-10s %s\n", _("Type"),
                 _("Device"), _("Target"), _("Source"));
    else
        vshPrint(ctl, "%-10s %s\n", _("Target"), _("Source"));

2301 2302 2303
    vshPrint(ctl, "------------------------------------------------\n");

    for (i = 0; i < ndisks; i++) {
2304 2305
        char *type;
        char *device;
2306 2307 2308 2309
        char *target;
        char *source;

        ctxt->node = disks[i];
2310 2311 2312 2313 2314 2315

        if (details) {
            type = virXPathString("string(./@type)", ctxt);
            device = virXPathString("string(./@device)", ctxt);
        }

2316 2317 2318 2319 2320 2321 2322 2323 2324
        target = virXPathString("string(./target/@dev)", ctxt);
        if (!target) {
            vshError(ctl, "unable to query block list");
            goto cleanup;
        }
        source = virXPathString("string(./source/@file"
                                "|./source/@dev"
                                "|./source/@dir"
                                "|./source/@name)", ctxt);
2325 2326 2327 2328 2329 2330 2331 2332 2333
        if (details) {
            vshPrint(ctl, "%-10s %-10s %-10s %s\n", type, device,
                     target, source ? source : "-");
            VIR_FREE(type);
            VIR_FREE(device);
        } else {
            vshPrint(ctl, "%-10s %s\n", target, source ? source : "-");
        }

2334 2335 2336 2337
        VIR_FREE(target);
        VIR_FREE(source);
    }

A
Alex Jia 已提交
2338
    ret = true;
2339 2340 2341 2342

cleanup:
    VIR_FREE(disks);
    virDomainFree(dom);
2343 2344 2345
    VIR_FREE(xml);
    xmlFreeDoc(xmldoc);
    xmlXPathFreeContext(ctxt);
2346 2347 2348
    return ret;
}

2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398
/*
 * "domiflist" command
 */
static const vshCmdInfo info_domiflist[] = {
    {"help", N_("list all domain virtual interfaces")},
    {"desc", N_("Get the summary of virtual interfaces for a domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_domiflist[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"inactive", VSH_OT_BOOL, 0,
     N_("get inactive rather than running configuration")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomiflist(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    bool ret = false;
    unsigned int flags = 0;
    char *xml = NULL;
    xmlDocPtr xmldoc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    int ninterfaces;
    xmlNodePtr *interfaces = NULL;
    int i;

    if (vshCommandOptBool(cmd, "inactive"))
        flags |= VIR_DOMAIN_XML_INACTIVE;

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

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

    xml = virDomainGetXMLDesc(dom, flags);
    if (!xml)
        goto cleanup;

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

    ninterfaces = virXPathNodeSet("./devices/interface", ctxt, &interfaces);
    if (ninterfaces < 0)
        goto cleanup;

T
Taku Izumi 已提交
2399 2400
    vshPrint(ctl, "%-10s %-10s %-10s %-11s %s\n", _("Interface"), _("Type"),
             _("Source"), _("Model"), _("MAC"));
2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421
    vshPrint(ctl, "-------------------------------------------------------\n");

    for (i = 0; i < ninterfaces; i++) {
        char *type = NULL;
        char *source = NULL;
        char *target = NULL;
        char *model = NULL;
        char *mac = NULL;

        ctxt->node = interfaces[i];
        type = virXPathString("string(./@type)", ctxt);

        source = virXPathString("string(./source/@bridge"
                                "|./source/@dev"
                                "|./source/@network"
                                "|./source/@name)", ctxt);

        target = virXPathString("string(./target/@dev)", ctxt);
        model = virXPathString("string(./model/@type)", ctxt);
        mac = virXPathString("string(./mac/@address)", ctxt);

T
Taku Izumi 已提交
2422
        vshPrint(ctl, "%-10s %-10s %-10s %-11s %-10s\n",
2423
                 target ? target : "-",
T
Taku Izumi 已提交
2424 2425
                 type,
                 source ? source : "-",
2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446
                 model ? model : "-",
                 mac ? mac : "-");

        VIR_FREE(type);
        VIR_FREE(source);
        VIR_FREE(target);
        VIR_FREE(model);
        VIR_FREE(mac);
    }

    ret = true;

cleanup:
    VIR_FREE(interfaces);
    virDomainFree(dom);
    VIR_FREE(xml);
    xmlFreeDoc(xmldoc);
    xmlXPathFreeContext(ctxt);
    return ret;
}

2447 2448 2449
/*
 * "suspend" command
 */
2450
static const vshCmdInfo info_suspend[] = {
2451 2452
    {"help", N_("suspend a domain")},
    {"desc", N_("Suspend a running domain.")},
2453
    {NULL, NULL}
2454 2455
};

2456
static const vshCmdOptDef opts_suspend[] = {
2457
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
2458
    {NULL, 0, 0, NULL}
2459 2460
};

E
Eric Blake 已提交
2461
static bool
2462
cmdSuspend(vshControl *ctl, const vshCmd *cmd)
2463
{
2464
    virDomainPtr dom;
2465
    const char *name;
E
Eric Blake 已提交
2466
    bool ret = true;
2467

2468
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2469
        return false;
2470

J
Jim Meyering 已提交
2471
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2472
        return false;
2473 2474

    if (virDomainSuspend(dom) == 0) {
2475
        vshPrint(ctl, _("Domain %s suspended\n"), name);
2476
    } else {
2477
        vshError(ctl, _("Failed to suspend domain %s"), name);
E
Eric Blake 已提交
2478
        ret = false;
2479
    }
2480

2481 2482 2483 2484
    virDomainFree(dom);
    return ret;
}

2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555
/*
 * "dompmsuspend" command
 */
static const vshCmdInfo info_dom_pm_suspend[] = {
    {"help", N_("suspend a domain for a given time duration")},
    {"desc", N_("Suspend a running domain for a given time duration.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_dom_pm_suspend[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"duration", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("duration in seconds")},
    {"target", VSH_OT_STRING, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
                                                "disk(Suspend-to-Disk), "
                                                "hybrid(Hybrid-Suspend)")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomPMSuspend(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    const char *name;
    bool ret = false;
    const char *target = NULL;
    unsigned int suspendTarget;
    unsigned long long duration = 0;

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

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    if (vshCommandOptULongLong(cmd, "duration", &duration) < 0) {
        vshError(ctl, _("Invalid duration argument"));
        goto cleanup;
    }

    if (vshCommandOptString(cmd, "target", &target) < 0) {
        vshError(ctl, _("Invalid target argument"));
        goto cleanup;
    }

    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"));
        goto cleanup;
    }

    if (virDomainPMSuspendForDuration(dom, suspendTarget, duration, 0) < 0) {
        vshError(ctl, _("Domain %s could not be suspended"),
                 virDomainGetName(dom));
        goto cleanup;
    }

    vshPrint(ctl, _("Domain %s successfully suspended"),
             virDomainGetName(dom));

    ret = true;

cleanup:
    virDomainFree(dom);
    return ret;
}

2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601
/*
 * "dompmwakeup" command
 */

static const vshCmdInfo info_dom_pm_wakeup[] = {
    {"help", N_("wakeup a domain suspended by dompmsuspend command")},
    {"desc", N_("Wakeup a domain previously suspended "
                "by dompmsuspend command.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_dom_pm_wakeup[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomPMWakeup(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    const char *name;
    bool ret = false;
    unsigned int flags = 0;

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

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    if (virDomainPMWakeup(dom, flags) < 0) {
        vshError(ctl, _("Domain %s could not be woken up"),
                 virDomainGetName(dom));
        goto cleanup;
    }

    vshPrint(ctl, _("Domain %s successfully woken up"),
             virDomainGetName(dom));

    ret = true;

cleanup:
    virDomainFree(dom);
    return ret;
}

2602 2603 2604
/*
 * "create" command
 */
2605
static const vshCmdInfo info_create[] = {
2606 2607
    {"help", N_("create a domain from an XML file")},
    {"desc", N_("Create a domain.")},
2608 2609 2610
    {NULL, NULL}
};

2611
static const vshCmdOptDef opts_create[] = {
2612
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
2613
#ifndef WIN32
2614
    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
2615
#endif
2616
    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
2617
    {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroy the guest when virsh disconnects")},
2618 2619 2620
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2621
static bool
2622
cmdCreate(vshControl *ctl, const vshCmd *cmd)
2623 2624
{
    virDomainPtr dom;
2625
    const char *from = NULL;
E
Eric Blake 已提交
2626
    bool ret = true;
2627
    char *buffer;
2628
#ifndef WIN32
2629
    int console = vshCommandOptBool(cmd, "console");
2630
#endif
2631
    unsigned int flags = VIR_DOMAIN_NONE;
2632

2633
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2634
        return false;
2635

2636
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
2637
        return false;
2638

2639
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
2640
        return false;
2641

2642 2643
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_START_PAUSED;
2644 2645
    if (vshCommandOptBool(cmd, "autodestroy"))
        flags |= VIR_DOMAIN_START_AUTODESTROY;
2646 2647

    dom = virDomainCreateXML(ctl->conn, buffer, flags);
2648
    VIR_FREE(buffer);
2649

2650
    if (dom != NULL) {
2651
        vshPrint(ctl, _("Domain %s created from %s\n"),
2652
                 virDomainGetName(dom), from);
2653
#ifndef WIN32
2654
        if (console)
2655
            cmdRunConsole(ctl, dom, NULL, 0);
2656
#endif
2657
        virDomainFree(dom);
2658
    } else {
2659
        vshError(ctl, _("Failed to create domain from %s"), from);
E
Eric Blake 已提交
2660
        ret = false;
2661 2662 2663 2664
    }
    return ret;
}

2665 2666 2667
/*
 * "define" command
 */
2668
static const vshCmdInfo info_define[] = {
2669 2670
    {"help", N_("define (but don't start) a domain from an XML file")},
    {"desc", N_("Define a domain.")},
2671 2672 2673
    {NULL, NULL}
};

2674
static const vshCmdOptDef opts_define[] = {
2675
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
2676 2677 2678
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2679
static bool
2680
cmdDefine(vshControl *ctl, const vshCmd *cmd)
2681 2682
{
    virDomainPtr dom;
2683
    const char *from = NULL;
E
Eric Blake 已提交
2684
    bool ret = true;
2685
    char *buffer;
2686

2687
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2688
        return false;
2689

2690
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
2691
        return false;
2692

2693
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
2694
        return false;
2695 2696

    dom = virDomainDefineXML(ctl->conn, buffer);
2697
    VIR_FREE(buffer);
2698

2699
    if (dom != NULL) {
2700
        vshPrint(ctl, _("Domain %s defined from %s\n"),
2701
                 virDomainGetName(dom), from);
2702
        virDomainFree(dom);
2703
    } else {
2704
        vshError(ctl, _("Failed to define domain from %s"), from);
E
Eric Blake 已提交
2705
        ret = false;
2706 2707 2708 2709 2710 2711 2712
    }
    return ret;
}

/*
 * "undefine" command
 */
2713
static const vshCmdInfo info_undefine[] = {
2714 2715 2716
    {"help", N_("undefine a domain")},
    {"desc",
     N_("Undefine an inactive domain, or convert persistent to transient.")},
2717 2718 2719
    {NULL, NULL}
};

2720
static const vshCmdOptDef opts_undefine[] = {
2721
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
2722
    {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
2723 2724 2725 2726 2727 2728 2729
    {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
     N_("remove associated storage volumes (comma separated list of targets "
        "or source paths) (see domblklist)")},
    {"remove-all-storage", VSH_OT_BOOL, 0,
     N_("remove all associated storage volumes (use with caution)")},
    {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE,
     N_("wipe data on the removed volumes")},
2730 2731
    {"snapshots-metadata", VSH_OT_BOOL, 0,
     N_("remove all domain snapshot metadata, if inactive")},
2732 2733 2734
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2735
static bool
2736
cmdUndefine(vshControl *ctl, const vshCmd *cmd)
2737 2738
{
    virDomainPtr dom;
2739
    bool ret = false;
2740
    const char *name = NULL;
2741
    /* Flags to attempt.  */
E
Eric Blake 已提交
2742
    unsigned int flags = 0;
2743 2744 2745
    /* User-requested actions.  */
    bool managed_save = vshCommandOptBool(cmd, "managed-save");
    bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
2746 2747 2748
    bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
    bool remove_storage = false;
    bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
2749
    /* Positive if these items exist.  */
2750
    int has_managed_save = 0;
2751 2752 2753 2754 2755
    int has_snapshots_metadata = 0;
    int has_snapshots = 0;
    /* True if undefine will not strand data, even on older servers.  */
    bool managed_save_safe = false;
    bool snapshots_safe = false;
2756
    int rc = -1;
2757
    int running;
2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774
    /* list of volumes to remove along with this domain */
    const char *volumes_arg = NULL;
    char *volumes = NULL;
    char **volume_tokens = NULL;
    char *volume_tok = NULL;
    int nvolume_tokens = 0;
    char *def = NULL;
    char *source = NULL;
    char *target = NULL;
    int vol_i;
    int tok_i;
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr *vol_nodes = NULL;
    int nvolumes = 0;
    virStorageVolPtr vol = NULL;
    bool vol_del_failed = false;
2775

2776
    if (managed_save) {
2777
        flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
2778 2779 2780 2781 2782 2783
        managed_save_safe = true;
    }
    if (snapshots_metadata) {
        flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
        snapshots_safe = true;
    }
2784

2785
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2786
        return false;
2787

2788
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2789
        return false;
2790

2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801
    /* check if a string that should contain list of volumes to remove is present */
    if (vshCommandOptString(cmd, "storage", &volumes_arg) > 0) {
        volumes = vshStrdup(ctl, volumes_arg);

        if (remove_all_storage) {
            vshError(ctl, _("Specified both --storage and --remove-all-storage"));
            goto cleanup;
        }
        remove_storage = true;
   }

2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820
    /* Do some flag manipulation.  The goal here is to disable bits
     * from flags to reduce the likelihood of a server rejecting
     * unknown flag bits, as well as to track conditions which are
     * safe by default for the given hypervisor and server version.  */
    running = virDomainIsActive(dom);
    if (running < 0) {
        virshReportError(ctl);
        goto cleanup;
    }
    if (!running) {
        /* Undefine with snapshots only fails for inactive domains,
         * and managed save only exists on inactive domains; if
         * running, then we don't want to remove anything.  */
        has_managed_save = virDomainHasManagedSaveImage(dom, 0);
        if (has_managed_save < 0) {
            if (last_error->code != VIR_ERR_NO_SUPPORT) {
                virshReportError(ctl);
                goto cleanup;
            }
2821 2822
            virFreeError(last_error);
            last_error = NULL;
2823
            has_managed_save = 0;
2824 2825
        }

2826 2827
        has_snapshots = virDomainSnapshotNum(dom, 0);
        if (has_snapshots < 0) {
2828 2829
            if (last_error->code != VIR_ERR_NO_SUPPORT) {
                virshReportError(ctl);
2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841
                goto cleanup;
            }
            virFreeError(last_error);
            last_error = NULL;
            has_snapshots = 0;
        }
        if (has_snapshots) {
            has_snapshots_metadata
                = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
            if (has_snapshots_metadata < 0) {
                /* The server did not know the new flag, assume that all
                   snapshots have metadata.  */
2842 2843
                virFreeError(last_error);
                last_error = NULL;
2844 2845 2846 2847 2848
                has_snapshots_metadata = has_snapshots;
            } else {
                /* The server knew the new flag, all aspects of
                 * undefineFlags are safe.  */
                managed_save_safe = snapshots_safe = true;
2849
            }
2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862
        }
    }
    if (!has_managed_save) {
        flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
        managed_save_safe = true;
    }
    if (has_snapshots == 0) {
        snapshots_safe = true;
    }
    if (has_snapshots_metadata == 0) {
        flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
        snapshots_safe = true;
    }
2863

2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876
    /* Stash domain description for later use */
    if (remove_storage || remove_all_storage) {
        if (running) {
            vshError(ctl, _("Storage volume deletion is supported only on stopped domains"));
            goto cleanup;
        }

        if (!(def = virDomainGetXMLDesc(dom, 0))) {
            vshError(ctl, _("Could not retrieve domain XML description"));
            goto cleanup;
        }
    }

2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890
    /* Generally we want to try the new API first.  However, while
     * virDomainUndefineFlags was introduced at the same time as
     * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
     * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present
     * until 0.9.5; skip to piecewise emulation if we couldn't prove
     * above that the new API is safe.  */
    if (managed_save_safe && snapshots_safe) {
        rc = virDomainUndefineFlags(dom, flags);
        if (rc == 0 || (last_error->code != VIR_ERR_NO_SUPPORT &&
                        last_error->code != VIR_ERR_INVALID_ARG))
            goto out;
        virFreeError(last_error);
        last_error = NULL;
    }
2891

2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903
    /* The new API is unsupported or unsafe; fall back to doing things
     * piecewise.  */
    if (has_managed_save) {
        if (!managed_save) {
            vshError(ctl, "%s",
                     _("Refusing to undefine while domain managed save "
                       "image exists"));
            goto cleanup;
        }
        if (virDomainManagedSaveRemove(dom, 0) < 0) {
            virshReportError(ctl);
            goto cleanup;
2904 2905 2906
        }
    }

2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920
    /* No way to emulate deletion of just snapshot metadata
     * without support for the newer flags.  Oh well.  */
    if (has_snapshots_metadata) {
        vshError(ctl,
                 snapshots_metadata ?
                 _("Unable to remove metadata of %d snapshots") :
                 _("Refusing to undefine while %d snapshots exist"),
                 has_snapshots_metadata);
        goto cleanup;
    }

    rc = virDomainUndefine(dom);

out:
2921
    if (rc == 0) {
2922
        vshPrint(ctl, _("Domain %s has been undefined\n"), name);
2923
        ret = true;
2924
    } else {
2925
        vshError(ctl, _("Failed to undefine domain %s"), name);
2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046
        goto cleanup;
    }

    /* try to undefine storage volumes associated with this domain, if it's requested */
    if (remove_storage || remove_all_storage) {
        ret = false;

        /* tokenize the string from user and save it's parts into an array */
        if (volumes) {
            /* count the delimiters */
            volume_tok = volumes;
            nvolume_tokens = 1; /* we need at least one member */
            while (*volume_tok) {
                if (*volume_tok == ',')
                    nvolume_tokens++;
                volume_tok++;
            }

            volume_tokens = vshCalloc(ctl, nvolume_tokens,  sizeof(char *));

            /* tokenize the input string */
            nvolume_tokens = 0;
            volume_tok = volumes;
            do {
                volume_tokens[nvolume_tokens] = strsep(&volume_tok, ",");
                nvolume_tokens++;
            } while (volume_tok);
        }

        doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
        if (!doc)
            goto cleanup;

        nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);

        if (nvolumes < 0)
            goto cleanup;

        for (vol_i = 0; vol_i < nvolumes; vol_i++) {
            ctxt->node = vol_nodes[vol_i];
            VIR_FREE(target);
            VIR_FREE(source);
            if (vol) {
                virStorageVolFree(vol);
                vol = NULL;
            }

            /* get volume source and target paths */
            if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
                vshError(ctl, _("Failed to enumerate devices"));
                goto cleanup;
            }

            if (!(source = virXPathString("string("
                                          "./source/@file|"
                                          "./source/@dir|"
                                          "./source/@name|"
                                          "./source/@dev)", ctxt)) &&
                virGetLastError())
                goto cleanup;

            /* lookup if volume was selected by user */
            if (volumes) {
                volume_tok = NULL;
                for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
                    if (volume_tokens[tok_i] &&
                        (STREQ_NULLABLE(volume_tokens[tok_i], target) ||
                         STREQ_NULLABLE(volume_tokens[tok_i], source))) {
                        volume_tok = volume_tokens[tok_i];
                        volume_tokens[tok_i] = NULL;
                        break;
                    }
                }
                if (!volume_tok)
                    continue;
            }

            if (!source)
                continue;

            if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
                vshPrint(ctl,
                         _("Storage volume '%s'(%s) is not managed by libvirt. "
                           "Remove it manually.\n"), target, source);
                virResetLastError();
                continue;
            }

            if (wipe_storage) {
                vshPrint(ctl, _("Wiping volume '%s'(%s) ... "), target, source);
                fflush(stdout);
                if (virStorageVolWipe(vol, 0) < 0) {
                    vshError(ctl, _("Failed! Volume not removed."));
                    vol_del_failed = true;
                    continue;
                } else {
                    vshPrint(ctl, _("Done.\n"));
                }
            }

            /* delete the volume */
            if (virStorageVolDelete(vol, 0) < 0) {
                vshError(ctl, _("Failed to remove storage volume '%s'(%s)"),
                         target, source);
                vol_del_failed = true;
            }
            vshPrint(ctl, _("Volume '%s' removed.\n"), volume_tok?volume_tok:source);
        }

        /* print volumes specified by user that were not found in domain definition */
        if (volumes) {
            for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
                if (volume_tokens[tok_i])
                    vshPrint(ctl, _("Volume '%s' was not found in domain's "
                                    "definition.\n"),
                             volume_tokens[tok_i]);
            }
        }

        if (!vol_del_failed)
            ret = true;
3047 3048
    }

3049
cleanup:
3050 3051 3052 3053 3054 3055
    VIR_FREE(source);
    VIR_FREE(target);
    VIR_FREE(volumes);
    VIR_FREE(volume_tokens);
    VIR_FREE(def);
    VIR_FREE(vol_nodes);
3056 3057
    if (vol)
        virStorageVolFree(vol);
3058 3059
    xmlFreeDoc(doc);
    xmlXPathFreeContext(ctxt);
3060
    virDomainFree(dom);
3061 3062 3063 3064 3065 3066 3067
    return ret;
}


/*
 * "start" command
 */
3068
static const vshCmdInfo info_start[] = {
3069
    {"help", N_("start a (previously defined) inactive domain")},
3070 3071 3072
    {"desc", N_("Start a domain, either from the last managedsave\n"
                "    state, or via a fresh boot if no managedsave state\n"
                "    is present.")},
3073 3074 3075
    {NULL, NULL}
};

3076
static const vshCmdOptDef opts_start[] = {
3077
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive domain")},
3078
#ifndef WIN32
3079
    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
3080
#endif
E
Eric Blake 已提交
3081
    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
3082 3083
    {"autodestroy", VSH_OT_BOOL, 0,
     N_("automatically destroy the guest when virsh disconnects")},
3084 3085
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when loading")},
3086 3087
    {"force-boot", VSH_OT_BOOL, 0,
     N_("force fresh boot by discarding any managed save")},
3088 3089 3090
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3091
static bool
3092
cmdStart(vshControl *ctl, const vshCmd *cmd)
3093 3094
{
    virDomainPtr dom;
3095
    bool ret = false;
3096
#ifndef WIN32
3097
    int console = vshCommandOptBool(cmd, "console");
3098
#endif
E
Eric Blake 已提交
3099
    unsigned int flags = VIR_DOMAIN_NONE;
3100
    int rc;
3101

3102
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3103
        return false;
3104

3105 3106
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
                                      VSH_BYNAME | VSH_BYUUID)))
E
Eric Blake 已提交
3107
        return false;
3108 3109

    if (virDomainGetID(dom) != (unsigned int)-1) {
3110
        vshError(ctl, "%s", _("Domain is already active"));
3111
        virDomainFree(dom);
E
Eric Blake 已提交
3112
        return false;
3113 3114
    }

E
Eric Blake 已提交
3115 3116
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_START_PAUSED;
3117 3118
    if (vshCommandOptBool(cmd, "autodestroy"))
        flags |= VIR_DOMAIN_START_AUTODESTROY;
3119 3120
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_START_BYPASS_CACHE;
3121 3122
    if (vshCommandOptBool(cmd, "force-boot"))
        flags |= VIR_DOMAIN_START_FORCE_BOOT;
E
Eric Blake 已提交
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
    /* We can emulate force boot, even for older servers that reject it.  */
    if (flags & VIR_DOMAIN_START_FORCE_BOOT) {
        if (virDomainCreateWithFlags(dom, flags) == 0)
            goto started;
        if (last_error->code != VIR_ERR_NO_SUPPORT &&
            last_error->code != VIR_ERR_INVALID_ARG) {
            virshReportError(ctl);
            goto cleanup;
        }
        virFreeError(last_error);
        last_error = NULL;
        rc = virDomainHasManagedSaveImage(dom, 0);
        if (rc < 0) {
            /* No managed save image to remove */
            virFreeError(last_error);
            last_error = NULL;
        } else if (rc > 0) {
            if (virDomainManagedSaveRemove(dom, 0) < 0) {
                virshReportError(ctl);
                goto cleanup;
            }
        }
        flags &= ~VIR_DOMAIN_START_FORCE_BOOT;
    }

E
Eric Blake 已提交
3149 3150
    /* Prefer older API unless we have to pass a flag.  */
    if ((flags ? virDomainCreateWithFlags(dom, flags)
3151
         : virDomainCreate(dom)) < 0) {
3152
        vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
3153
        goto cleanup;
3154
    }
3155 3156 3157 3158 3159

started:
    vshPrint(ctl, _("Domain %s started\n"),
             virDomainGetName(dom));
#ifndef WIN32
3160
    if (console && !cmdRunConsole(ctl, dom, NULL, 0))
3161 3162 3163 3164 3165 3166
        goto cleanup;
#endif

    ret = true;

cleanup:
3167
    virDomainFree(dom);
3168 3169 3170
    return ret;
}

3171 3172 3173
/*
 * "save" command
 */
3174
static const vshCmdInfo info_save[] = {
3175
    {"help", N_("save a domain state to a file")},
E
Eric Blake 已提交
3176
    {"desc", N_("Save the RAM state of a running domain.")},
3177
    {NULL, NULL}
3178 3179
};

3180
static const vshCmdOptDef opts_save[] = {
3181
    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
3182 3183
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to save the data")},
3184 3185
    {"xml", VSH_OT_STRING, 0,
     N_("filename containing updated XML for the target")},
3186 3187
    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
3188
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
3189
    {NULL, 0, 0, NULL}
3190 3191
};

3192 3193
static void
doSave(void *opaque)
3194
{
3195 3196 3197 3198 3199
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
    char ret = '1';
    virDomainPtr dom = NULL;
3200 3201
    const char *name = NULL;
    const char *to = NULL;
E
Eric Blake 已提交
3202
    unsigned int flags = 0;
3203 3204
    const char *xmlfile = NULL;
    char *xml = NULL;
3205 3206 3207 3208 3209 3210
    sigset_t sigmask, oldsigmask;

    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
        goto out_sig;
3211

3212
    if (!vshConnectionUsability(ctl, ctl->conn))
3213
        goto out;
3214

3215
    if (vshCommandOptString(cmd, "file", &to) <= 0)
3216
        goto out;
3217

3218 3219
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
3220 3221 3222 3223
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
3224

3225 3226
    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
        vshError(ctl, "%s", _("malformed xml argument"));
3227
        goto out;
3228 3229
    }

J
Jim Meyering 已提交
3230
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
3231
        goto out;
3232

3233 3234
    if (xmlfile &&
        virFileReadAll(xmlfile, 8192, &xml) < 0)
3235
        goto out;
3236 3237 3238

    if (((flags || xml)
         ? virDomainSaveFlags(dom, to, xml, flags)
3239
         : virDomainSave(dom, to)) < 0) {
3240
        vshError(ctl, _("Failed to save domain %s to %s"), name, to);
3241
        goto out;
3242
    }
3243

3244
    ret = '0';
3245

3246 3247 3248 3249
out:
    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
out_sig:
    if (dom) virDomainFree (dom);
3250
    VIR_FREE(xml);
3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297
    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
}

static bool
cmdSave(vshControl *ctl, const vshCmd *cmd)
{
    bool ret = false;
    virDomainPtr dom = NULL;
    int p[2] = {-1. -1};
    virThread workerThread;
    bool verbose = false;
    vshCtrlData data;
    const char *to = NULL;
    const char *name = NULL;

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    if (vshCommandOptString(cmd, "file", &to) <= 0)
        goto cleanup;

    if (vshCommandOptBool (cmd, "verbose"))
        verbose = true;

    if (pipe(p) < 0)
        goto cleanup;

    data.ctl = ctl;
    data.cmd = cmd;
    data.writefd = p[1];

    if (virThreadCreate(&workerThread,
                        true,
                        doSave,
                        &data) < 0)
        goto cleanup;

    ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Save"));

    virThreadJoin(&workerThread);

    if (ret)
        vshPrint(ctl, _("\nDomain %s saved to %s\n"), name, to);

cleanup:
    if (dom)
        virDomainFree(dom);
3298 3299 3300
    return ret;
}

3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321
/*
 * "save-image-dumpxml" command
 */
static const vshCmdInfo info_save_image_dumpxml[] = {
    {"help", N_("saved state domain information in XML")},
    {"desc", N_("Output the domain information for a saved state file,\n"
                "as an XML dump to stdout.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_save_image_dumpxml[] = {
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to read")},
    {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
    {NULL, 0, 0, NULL}
};

static bool
cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    bool ret = false;
E
Eric Blake 已提交
3322
    unsigned int flags = 0;
3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358
    char *xml = NULL;

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

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

    if (vshCommandOptString(cmd, "file", &file) <= 0)
        return false;

    xml = virDomainSaveImageGetXMLDesc(ctl->conn, file, flags);
    if (!xml)
        goto cleanup;

    vshPrint(ctl, "%s", xml);
    ret = true;

cleanup:
    VIR_FREE(xml);
    return ret;
}

/*
 * "save-image-define" command
 */
static const vshCmdInfo info_save_image_define[] = {
    {"help", N_("redefine the XML for a domain's saved state file")},
    {"desc", N_("Replace the domain XML associated with a saved state file")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_save_image_define[] = {
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to modify")},
    {"xml", VSH_OT_STRING, VSH_OFLAG_REQ,
     N_("filename containing updated XML for the target")},
3359 3360
    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
3361 3362 3363 3364 3365 3366 3367 3368 3369 3370
    {NULL, 0, 0, NULL}
};

static bool
cmdSaveImageDefine(vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    bool ret = false;
    const char *xmlfile = NULL;
    char *xml = NULL;
3371 3372 3373 3374 3375 3376
    unsigned int flags = 0;

    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391

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

    if (vshCommandOptString(cmd, "file", &file) <= 0)
        return false;

    if (vshCommandOptString(cmd, "xml", &xmlfile) <= 0) {
        vshError(ctl, "%s", _("malformed or missing xml argument"));
        return false;
    }

    if (virFileReadAll(xmlfile, 8192, &xml) < 0)
        goto cleanup;

3392
    if (virDomainSaveImageDefineXML(ctl->conn, file, xml, flags) < 0) {
3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415
        vshError(ctl, _("Failed to update %s"), file);
        goto cleanup;
    }

    vshPrint(ctl, _("State file %s updated.\n"), file);
    ret = true;

cleanup:
    VIR_FREE(xml);
    return ret;
}

/*
 * "save-image-edit" command
 */
static const vshCmdInfo info_save_image_edit[] = {
    {"help", N_("edit XML for a domain's saved state file")},
    {"desc", N_("Edit the domain XML associated with a saved state file")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_save_image_edit[] = {
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to edit")},
3416 3417
    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428
    {NULL, 0, 0, NULL}
};

static bool
cmdSaveImageEdit(vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    bool ret = false;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444
    unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
    unsigned int define_flags = 0;

    if (vshCommandOptBool(cmd, "running"))
        define_flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        define_flags |= VIR_DOMAIN_SAVE_PAUSED;

    /* Normally, we let the API reject mutually exclusive flags.
     * However, in the edit cycle, we let the user retry if the define
     * step fails, but the define step will always fail on invalid
     * flags, so we reject it up front to avoid looping.  */
    if (define_flags == (VIR_DOMAIN_SAVE_RUNNING | VIR_DOMAIN_SAVE_PAUSED)) {
        vshError(ctl, "%s", _("--running and --saved are mutually exclusive"));
        return false;
    }
3445 3446 3447 3448 3449 3450 3451 3452

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

    if (vshCommandOptString(cmd, "file", &file) <= 0)
        return false;

    /* Get the XML configuration of the saved image.  */
3453
    doc = virDomainSaveImageGetXMLDesc(ctl->conn, file, getxml_flags);
3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470
    if (!doc)
        goto cleanup;

    /* Create and open the temporary file.  */
    tmp = editWriteToTempFile(ctl, doc);
    if (!tmp)
        goto cleanup;

    /* Start the editor.  */
    if (editFile(ctl, tmp) == -1)
        goto cleanup;

    /* Read back the edited file.  */
    doc_edited = editReadBackFile(ctl, tmp);
    if (!doc_edited)
        goto cleanup;

3471 3472 3473
    /* Compare original XML with edited.  Short-circuit if it did not
     * change, and we do not have any flags.  */
    if (STREQ(doc, doc_edited) && !define_flags) {
3474 3475 3476 3477 3478 3479 3480
        vshPrint(ctl, _("Saved image %s XML configuration not changed.\n"),
                 file);
        ret = true;
        goto cleanup;
    }

    /* Everything checks out, so redefine the xml.  */
3481 3482
    if (virDomainSaveImageDefineXML(ctl->conn, file, doc_edited,
                                    define_flags) < 0) {
3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499
        vshError(ctl, _("Failed to update %s"), file);
        goto cleanup;
    }

    vshPrint(ctl, _("State file %s edited.\n"), file);
    ret = true;

cleanup:
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    if (tmp) {
        unlink(tmp);
        VIR_FREE(tmp);
    }
    return ret;
}

3500 3501 3502 3503 3504
/*
 * "managedsave" command
 */
static const vshCmdInfo info_managedsave[] = {
    {"help", N_("managed save of a domain state")},
3505 3506 3507 3508
    {"desc", N_("Save and destroy a running domain, so it can be restarted from\n"
                "    the same state at a later time.  When the virsh 'start'\n"
                "    command is next run for the domain, it will automatically\n"
                "    be started from this saved state.")},
3509 3510 3511 3512
    {NULL, NULL}
};

static const vshCmdOptDef opts_managedsave[] = {
3513
    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
3514
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3515 3516
    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on next start")},
    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on next start")},
3517
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
3518 3519 3520
    {NULL, 0, 0, NULL}
};

3521 3522
static void
doManagedsave(void *opaque)
3523
{
3524 3525 3526 3527 3528
    char ret = '1';
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
    virDomainPtr dom = NULL;
3529
    const char *name;
E
Eric Blake 已提交
3530
    unsigned int flags = 0;
3531 3532 3533 3534 3535 3536
    sigset_t sigmask, oldsigmask;

    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
        goto out_sig;
3537

3538
    if (!vshConnectionUsability(ctl, ctl->conn))
3539
        goto out;
3540

3541 3542
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
3543 3544 3545 3546
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
3547

3548
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
3549
        goto out;
3550

3551
    if (virDomainManagedSave(dom, flags) < 0) {
3552
        vshError(ctl, _("Failed to save domain %s state"), name);
3553
        goto out;
3554 3555
    }

3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601
    ret = '0';
out:
    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
out_sig:
    if (dom)
        virDomainFree (dom);
    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
}

static bool
cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    int p[2] = { -1, -1};
    bool ret = false;
    bool verbose = false;
    const char *name = NULL;
    vshCtrlData data;
    virThread workerThread;

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    if (vshCommandOptBool (cmd, "verbose"))
        verbose = true;

    if (pipe(p) < 0)
        goto cleanup;

    data.ctl = ctl;
    data.cmd = cmd;
    data.writefd = p[1];

    if (virThreadCreate(&workerThread,
                        true,
                        doManagedsave,
                        &data) < 0)
        goto cleanup;

    ret = vshWatchJob(ctl, dom, verbose, p[0], 0,
                      NULL, NULL, _("Managedsave"));

    virThreadJoin(&workerThread);

    if (ret)
        vshPrint(ctl, _("\nDomain %s state saved by libvirt\n"), name);
3602 3603

cleanup:
3604
    virDomainFree(dom);
3605 3606
    VIR_FORCE_CLOSE(p[0]);
    VIR_FORCE_CLOSE(p[1]);
3607 3608 3609
    return ret;
}

3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623
/*
 * "managedsave-remove" command
 */
static const vshCmdInfo info_managedsaveremove[] = {
    {"help", N_("Remove managed save of a domain")},
    {"desc", N_("Remove an existing managed save state file from a domain")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_managedsaveremove[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3624
static bool
3625 3626 3627
cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
3628
    const char *name;
E
Eric Blake 已提交
3629
    bool ret = false;
3630 3631
    int hassave;

3632
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3633
        return false;
3634 3635

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3636
        return false;
3637 3638 3639

    hassave = virDomainHasManagedSaveImage(dom, 0);
    if (hassave < 0) {
3640
        vshError(ctl, "%s", _("Failed to check for domain managed save image"));
3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656
        goto cleanup;
    }

    if (hassave) {
        if (virDomainManagedSaveRemove(dom, 0) < 0) {
            vshError(ctl, _("Failed to remove managed save image for domain %s"),
                     name);
            goto cleanup;
        }
        else
            vshPrint(ctl, _("Removed managedsave image for domain %s"), name);
    }
    else
        vshPrint(ctl, _("Domain %s has no manage save image; removal skipped"),
                 name);

E
Eric Blake 已提交
3657
    ret = true;
3658 3659 3660 3661 3662 3663

cleanup:
    virDomainFree(dom);
    return ret;
}

3664 3665 3666
/*
 * "schedinfo" command
 */
3667
static const vshCmdInfo info_schedinfo[] = {
3668 3669
    {"help", N_("show/set scheduler parameters")},
    {"desc", N_("Show/Set scheduler parameters.")},
3670 3671 3672
    {NULL, NULL}
};

3673
static const vshCmdOptDef opts_schedinfo[] = {
3674 3675 3676 3677
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"set", VSH_OT_STRING, VSH_OFLAG_NONE, N_("parameter=value")},
    {"weight", VSH_OT_INT, VSH_OFLAG_NONE, N_("weight for XEN_CREDIT")},
    {"cap", VSH_OT_INT, VSH_OFLAG_NONE, N_("cap for XEN_CREDIT")},
3678 3679 3680
    {"current", VSH_OT_BOOL, 0, N_("get/set current scheduler info")},
    {"config", VSH_OT_BOOL, 0, N_("get/set value to be used on next boot")},
    {"live", VSH_OT_BOOL, 0, N_("get/set value from running domain")},
3681 3682 3683
    {NULL, 0, 0, NULL}
};

W
Wen Congyang 已提交
3684
static int
3685
cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
3686
                   virTypedParameterPtr param)
3687
{
3688
    const char *data = NULL;
3689 3690 3691

    /* Legacy 'weight' parameter */
    if (STREQ(param->field, "weight") &&
3692
        param->type == VIR_TYPED_PARAM_UINT &&
3693 3694
        vshCommandOptBool(cmd, "weight")) {
        int val;
3695
        if (vshCommandOptInt(cmd, "weight", &val) <= 0) {
3696
            vshError(ctl, "%s", _("Invalid value of weight"));
3697
            return -1;
3698
        } else {
3699
            param->value.ui = val;
3700
        }
3701
        return 1;
3702 3703
    }

3704 3705
    /* Legacy 'cap' parameter */
    if (STREQ(param->field, "cap") &&
3706
        param->type == VIR_TYPED_PARAM_UINT &&
3707 3708
        vshCommandOptBool(cmd, "cap")) {
        int val;
3709
        if (vshCommandOptInt(cmd, "cap", &val) <= 0) {
3710
            vshError(ctl, "%s", _("Invalid value of cap"));
3711
            return -1;
3712
        } else {
3713
            param->value.ui = val;
3714
        }
3715
        return 1;
3716
    }
3717

3718
    if (vshCommandOptString(cmd, "set", &data) > 0) {
3719 3720 3721
        char *val = strchr(data, '=');
        int match = 0;
        if (!val) {
3722
            vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
3723
            return -1;
3724
        }
3725 3726 3727 3728 3729 3730 3731 3732 3733
        *val = '\0';
        match = STREQ(data, param->field);
        *val = '=';
        val++;

        if (!match)
            return 0;

        switch (param->type) {
3734
        case VIR_TYPED_PARAM_INT:
3735
            if (virStrToLong_i(val, NULL, 10, &param->value.i) < 0) {
3736
                vshError(ctl, "%s",
3737 3738 3739 3740
                         _("Invalid value for parameter, expecting an int"));
                return -1;
            }
            break;
3741
        case VIR_TYPED_PARAM_UINT:
3742
            if (virStrToLong_ui(val, NULL, 10, &param->value.ui) < 0) {
3743
                vshError(ctl, "%s",
3744 3745 3746 3747
                         _("Invalid value for parameter, expecting an unsigned int"));
                return -1;
            }
            break;
3748
        case VIR_TYPED_PARAM_LLONG:
3749
            if (virStrToLong_ll(val, NULL, 10, &param->value.l) < 0) {
3750
                vshError(ctl, "%s",
J
Jim Meyering 已提交
3751
                         _("Invalid value for parameter, expecting a long long"));
3752 3753 3754
                return -1;
            }
            break;
3755
        case VIR_TYPED_PARAM_ULLONG:
3756
            if (virStrToLong_ull(val, NULL, 10, &param->value.ul) < 0) {
3757
                vshError(ctl, "%s",
3758 3759 3760 3761
                         _("Invalid value for parameter, expecting an unsigned long long"));
                return -1;
            }
            break;
3762
        case VIR_TYPED_PARAM_DOUBLE:
3763
            if (virStrToDouble(val, NULL, &param->value.d) < 0) {
3764
                vshError(ctl, "%s", _("Invalid value for parameter, expecting a double"));
3765 3766 3767
                return -1;
            }
            break;
3768
        case VIR_TYPED_PARAM_BOOLEAN:
3769
            param->value.b = STREQ(val, "0") ? 0 : 1;
3770
        }
3771
        return 1;
3772
    }
3773

3774 3775
    return 0;
}
3776

3777

E
Eric Blake 已提交
3778
static bool
3779 3780 3781 3782
cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
{
    char *schedulertype;
    virDomainPtr dom;
3783
    virTypedParameterPtr params = NULL;
3784 3785 3786
    int nparams = 0;
    int update = 0;
    int i, ret;
W
Wen Congyang 已提交
3787
    bool ret_val = false;
3788 3789 3790 3791 3792 3793 3794 3795 3796 3797
    unsigned int flags = 0;
    int current = vshCommandOptBool(cmd, "current");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
3798
        flags = VIR_DOMAIN_AFFECT_CURRENT;
3799 3800
    } else {
        if (config)
3801
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
3802
        if (live)
3803
            flags |= VIR_DOMAIN_AFFECT_LIVE;
3804
    }
3805

3806
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3807
        return false;
3808

3809
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
3810
        return false;
3811 3812 3813

    /* Print SchedulerType */
    schedulertype = virDomainGetSchedulerType(dom, &nparams);
3814
    if (schedulertype != NULL) {
3815
        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"),
3816
             schedulertype);
3817
        VIR_FREE(schedulertype);
3818
    } else {
3819
        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
3820
        goto cleanup;
3821 3822
    }

3823
    if (nparams) {
3824
        params = vshMalloc(ctl, sizeof(*params) * nparams);
3825

3826
        memset(params, 0, sizeof(*params) * nparams);
3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837
        if (flags || current) {
            /* We cannot query both live and config at once, so settle
               on current in that case.  If we are setting, then the
               two values should match when we re-query; otherwise, we
               report the error later.  */
            ret = virDomainGetSchedulerParametersFlags(dom, params, &nparams,
                                                       ((live && config) ? 0
                                                        : flags));
        } else {
            ret = virDomainGetSchedulerParameters(dom, params, &nparams);
        }
3838 3839 3840 3841
        if (ret == -1)
            goto cleanup;

        /* See if any params are being set */
3842
        for (i = 0; i < nparams; i++) {
3843 3844 3845 3846 3847 3848 3849 3850 3851 3852
            ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
            if (ret == -1)
                goto cleanup;

            if (ret == 1)
                update = 1;
        }

        /* Update parameters & refresh data */
        if (update) {
3853 3854 3855 3856 3857
            if (flags || current)
                ret = virDomainSetSchedulerParametersFlags(dom, params,
                                                           nparams, flags);
            else
                ret = virDomainSetSchedulerParameters(dom, params, nparams);
3858 3859 3860
            if (ret == -1)
                goto cleanup;

3861 3862 3863 3864 3865 3866 3867
            if (flags || current)
                ret = virDomainGetSchedulerParametersFlags(dom, params,
                                                           &nparams,
                                                           ((live && config) ? 0
                                                            : flags));
            else
                ret = virDomainGetSchedulerParameters(dom, params, &nparams);
3868 3869
            if (ret == -1)
                goto cleanup;
3870 3871 3872 3873
        } else {
            /* See if we've tried to --set var=val.  If so, the fact that
               we reach this point (with update == 0) means that "var" did
               not match any of the settable parameters.  Report the error.  */
3874 3875
            const char *var_value_pair = NULL;
            if (vshCommandOptString(cmd, "set", &var_value_pair) > 0) {
3876 3877 3878 3879
                vshError(ctl, _("invalid scheduler option: %s"),
                         var_value_pair);
                goto cleanup;
            }
3880 3881 3882 3883 3884 3885
            /* When not doing --set, --live and --config do not mix.  */
            if (live && config) {
                vshError(ctl, "%s",
                         _("cannot query both live and config at once"));
                goto cleanup;
            }
3886 3887
        }

E
Eric Blake 已提交
3888
        ret_val = true;
3889
        for (i = 0; i < nparams; i++) {
3890 3891 3892
            char *str = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
            VIR_FREE(str);
3893 3894
        }
    }
3895

3896
 cleanup:
3897
    VIR_FREE(params);
3898
    virDomainFree(dom);
3899
    return ret_val;
3900 3901
}

3902 3903 3904
/*
 * "restore" command
 */
3905
static const vshCmdInfo info_restore[] = {
3906 3907
    {"help", N_("restore a domain from a saved state in a file")},
    {"desc", N_("Restore a domain.")},
3908
    {NULL, NULL}
3909 3910
};

3911
static const vshCmdOptDef opts_restore[] = {
3912
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the state to restore")},
3913 3914
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when restoring")},
3915 3916
    {"xml", VSH_OT_STRING, 0,
     N_("filename containing updated XML for the target")},
3917 3918
    {"running", VSH_OT_BOOL, 0, N_("restore domain into running state")},
    {"paused", VSH_OT_BOOL, 0, N_("restore domain into paused state")},
3919
    {NULL, 0, 0, NULL}
3920 3921
};

E
Eric Blake 已提交
3922
static bool
3923
cmdRestore(vshControl *ctl, const vshCmd *cmd)
3924
{
3925
    const char *from = NULL;
3926
    bool ret = false;
E
Eric Blake 已提交
3927
    unsigned int flags = 0;
3928 3929
    const char *xmlfile = NULL;
    char *xml = NULL;
3930

3931
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3932
        return false;
3933

3934
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
3935
        return false;
3936

3937 3938
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
3939 3940 3941 3942
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
3943

3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954
    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
        vshError(ctl, "%s", _("malformed xml argument"));
        return false;
    }

    if (xmlfile &&
        virFileReadAll(xmlfile, 8192, &xml) < 0)
        goto cleanup;

    if (((flags || xml)
         ? virDomainRestoreFlags(ctl->conn, from, xml, flags)
3955
         : virDomainRestore(ctl->conn, from)) < 0) {
3956
        vshError(ctl, _("Failed to restore domain from %s"), from);
3957
        goto cleanup;
3958
    }
3959 3960 3961 3962 3963

    vshPrint(ctl, _("Domain restored from %s\n"), from);
    ret = true;

cleanup:
3964
    VIR_FREE(xml);
3965 3966 3967
    return ret;
}

D
Daniel Veillard 已提交
3968 3969 3970
/*
 * "dump" command
 */
3971
static const vshCmdInfo info_dump[] = {
3972 3973
    {"help", N_("dump the core of a domain to a file for analysis")},
    {"desc", N_("Core dump a domain.")},
D
Daniel Veillard 已提交
3974 3975 3976
    {NULL, NULL}
};

3977
static const vshCmdOptDef opts_dump[] = {
3978 3979
    {"live", VSH_OT_BOOL, 0, N_("perform a live core dump if supported")},
    {"crash", VSH_OT_BOOL, 0, N_("crash the domain after core dump")},
3980 3981
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when saving")},
3982
    {"reset", VSH_OT_BOOL, 0, N_("reset the domain after core dump")},
3983 3984
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to dump the core")},
3985
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of dump")},
D
Daniel Veillard 已提交
3986 3987 3988
    {NULL, 0, 0, NULL}
};

3989 3990
static void
doDump(void *opaque)
D
Daniel Veillard 已提交
3991
{
3992 3993 3994 3995 3996 3997
    char ret = '1';
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
    virDomainPtr dom = NULL;
    sigset_t sigmask, oldsigmask;
3998 3999
    const char *name = NULL;
    const char *to = NULL;
E
Eric Blake 已提交
4000
    unsigned int flags = 0;
D
Daniel Veillard 已提交
4001

4002 4003 4004 4005 4006
    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
        goto out_sig;

4007
    if (!vshConnectionUsability(ctl, ctl->conn))
4008
        goto out;
D
Daniel Veillard 已提交
4009

4010
    if (vshCommandOptString(cmd, "file", &to) <= 0)
4011
        goto out;
D
Daniel Veillard 已提交
4012

J
Jim Meyering 已提交
4013
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
4014
        goto out;
D
Daniel Veillard 已提交
4015

P
Paolo Bonzini 已提交
4016 4017
    if (vshCommandOptBool (cmd, "live"))
        flags |= VIR_DUMP_LIVE;
4018 4019
    if (vshCommandOptBool (cmd, "crash"))
        flags |= VIR_DUMP_CRASH;
4020 4021
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DUMP_BYPASS_CACHE;
4022 4023
    if (vshCommandOptBool(cmd, "reset"))
        flags |= VIR_DUMP_RESET;
4024

4025
    if (virDomainCoreDump(dom, to, flags) < 0) {
4026
        vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
4027
        goto out;
D
Daniel Veillard 已提交
4028 4029
    }

4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078
    ret = '0';
out:
    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
out_sig:
    if (dom)
        virDomainFree (dom);
    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
}

static bool
cmdDump(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    int p[2] = { -1, -1};
    bool ret = false;
    bool verbose = false;
    const char *name = NULL;
    const char *to = NULL;
    vshCtrlData data;
    virThread workerThread;

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    if (vshCommandOptString(cmd, "file", &to) <= 0)
        return false;

    if (vshCommandOptBool (cmd, "verbose"))
        verbose = true;

    if (pipe(p) < 0)
        goto cleanup;

    data.ctl = ctl;
    data.cmd = cmd;
    data.writefd = p[1];

    if (virThreadCreate(&workerThread,
                        true,
                        doDump,
                        &data) < 0)
        goto cleanup;

    ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Dump"));

    virThreadJoin(&workerThread);

    if (ret)
        vshPrint(ctl, _("\nDomain %s dumped to %s\n"), name, to);
4079 4080

cleanup:
D
Daniel Veillard 已提交
4081
    virDomainFree(dom);
4082 4083
    VIR_FORCE_CLOSE(p[0]);
    VIR_FORCE_CLOSE(p[1]);
D
Daniel Veillard 已提交
4084 4085 4086
    return ret;
}

4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160
static const vshCmdInfo info_screenshot[] = {
    {"help", N_("take a screenshot of a current domain console and store it "
                "into a file")},
    {"desc", N_("screenshot of a current domain console")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_screenshot[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file", VSH_OT_DATA, VSH_OFLAG_NONE, N_("where to store the screenshot")},
    {"screen", VSH_OT_INT, VSH_OFLAG_NONE, N_("ID of a screen to take screenshot of")},
    {NULL, 0, 0, NULL}
};

static int vshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
                         const char *bytes, size_t nbytes, void *opaque)
{
    int *fd = opaque;

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

/**
 * Generate string: '<domain name>-<timestamp>[<extension>]'
 */
static char *
vshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
{
    char timestr[100];
    struct timeval cur_time;
    struct tm time_info;
    const char *ext = NULL;
    char *ret = NULL;

    /* We should be already connected, but doesn't
     * hurt to check */
    if (!vshConnectionUsability(ctl, ctl->conn))
        return NULL;

    if (!dom) {
        vshError(ctl, "%s", _("Invalid domain supplied"));
        return NULL;
    }

    if (STREQ(mime, "image/x-portable-pixmap"))
        ext = ".ppm";
    else if (STREQ(mime, "image/png"))
        ext = ".png";
    /* add mime type here */

    gettimeofday(&cur_time, NULL);
    localtime_r(&cur_time.tv_sec, &time_info);
    strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);

    if (virAsprintf(&ret, "%s-%s%s", virDomainGetName(dom),
                    timestr, ext ? ext : "") < 0) {
        vshError(ctl, "%s", _("Out of memory"));
        return NULL;
    }

    return ret;
}

static bool
cmdScreenshot(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    const char *name = NULL;
    char *file = NULL;
    int fd = -1;
    virStreamPtr st = NULL;
    unsigned int screen = 0;
    unsigned int flags = 0; /* currently unused */
    int ret = false;
4161
    bool created = false;
4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200
    bool generated = false;
    char *mime = NULL;

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

    if (vshCommandOptString(cmd, "file", (const char **) &file) < 0) {
        vshError(ctl, "%s", _("file must not be empty"));
        return false;
    }

    if (vshCommandOptUInt(cmd, "screen", &screen) < 0) {
        vshError(ctl, "%s", _("invalid screen ID"));
        return false;
    }

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    st = virStreamNew(ctl->conn, 0);

    mime = virDomainScreenshot(dom, st, screen, flags);
    if (!mime) {
        vshError(ctl, _("could not take a screenshot of %s"), name);
        goto cleanup;
    }

    if (!file) {
        if (!(file=vshGenFileName(ctl, dom, mime)))
            return false;
        generated = true;
    }

    if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
        if (errno != EEXIST ||
            (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
            vshError(ctl, _("cannot create file %s"), file);
            goto cleanup;
        }
4201 4202
    } else {
        created = true;
4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231
    }

    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
        vshError(ctl, _("could not receive data from domain %s"), name);
        goto cleanup;
    }

    if (VIR_CLOSE(fd) < 0) {
        vshError(ctl, _("cannot close file %s"), file);
        goto cleanup;
    }

    if (virStreamFinish(st) < 0) {
        vshError(ctl, _("cannot close stream on domain %s"), name);
        goto cleanup;
    }

    vshPrint(ctl, _("Screenshot saved to %s, with type of %s"), file, mime);
    ret = true;

cleanup:
    if (!ret && created)
        unlink(file);
    if (generated)
        VIR_FREE(file);
    virDomainFree(dom);
    if (st)
        virStreamFree(st);
    VIR_FORCE_CLOSE(fd);
4232
    VIR_FREE(mime);
4233 4234 4235
    return ret;
}

4236 4237 4238
/*
 * "resume" command
 */
4239
static const vshCmdInfo info_resume[] = {
4240 4241
    {"help", N_("resume a domain")},
    {"desc", N_("Resume a previously suspended domain.")},
4242
    {NULL, NULL}
4243 4244
};

4245
static const vshCmdOptDef opts_resume[] = {
4246
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4247
    {NULL, 0, 0, NULL}
4248 4249
};

E
Eric Blake 已提交
4250
static bool
4251
cmdResume(vshControl *ctl, const vshCmd *cmd)
4252
{
4253
    virDomainPtr dom;
E
Eric Blake 已提交
4254
    bool ret = true;
4255
    const char *name;
4256

4257
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4258
        return false;
4259

J
Jim Meyering 已提交
4260
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
4261
        return false;
4262 4263

    if (virDomainResume(dom) == 0) {
4264
        vshPrint(ctl, _("Domain %s resumed\n"), name);
4265
    } else {
4266
        vshError(ctl, _("Failed to resume domain %s"), name);
E
Eric Blake 已提交
4267
        ret = false;
4268
    }
4269

4270 4271 4272 4273
    virDomainFree(dom);
    return ret;
}

4274 4275 4276
/*
 * "shutdown" command
 */
4277
static const vshCmdInfo info_shutdown[] = {
4278 4279
    {"help", N_("gracefully shutdown a domain")},
    {"desc", N_("Run shutdown in the target domain.")},
4280
    {NULL, NULL}
4281 4282
};

4283
static const vshCmdOptDef opts_shutdown[] = {
4284
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4285
    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
4286
    {NULL, 0, 0, NULL}
4287 4288
};

E
Eric Blake 已提交
4289
static bool
4290
cmdShutdown(vshControl *ctl, const vshCmd *cmd)
4291
{
4292
    virDomainPtr dom;
E
Eric Blake 已提交
4293
    bool ret = true;
4294
    const char *name;
4295 4296 4297
    const char *mode = NULL;
    int flags = 0;
    int rv;
4298

4299
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4300
        return false;
4301

4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317
    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
        vshError(ctl, "%s", _("Invalid type"));
        return false;
    }

    if (mode) {
        if (STREQ(mode, "acpi")) {
            flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
        } else if (STREQ(mode, "agent")) {
            flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
        } else {
            vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
            return false;
        }
    }

J
Jim Meyering 已提交
4318
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
4319
        return false;
4320

4321 4322 4323 4324 4325
    if (flags)
        rv = virDomainShutdownFlags(dom, flags);
    else
        rv = virDomainShutdown(dom);
    if (rv == 0) {
4326
        vshPrint(ctl, _("Domain %s is being shutdown\n"), name);
4327
    } else {
4328
        vshError(ctl, _("Failed to shutdown domain %s"), name);
E
Eric Blake 已提交
4329
        ret = false;
4330
    }
4331

4332 4333 4334 4335
    virDomainFree(dom);
    return ret;
}

4336 4337 4338
/*
 * "reboot" command
 */
4339
static const vshCmdInfo info_reboot[] = {
4340 4341
    {"help", N_("reboot a domain")},
    {"desc", N_("Run a reboot command in the target domain.")},
4342 4343 4344
    {NULL, NULL}
};

4345
static const vshCmdOptDef opts_reboot[] = {
4346
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4347
    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
4348 4349 4350
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4351
static bool
4352
cmdReboot(vshControl *ctl, const vshCmd *cmd)
4353 4354
{
    virDomainPtr dom;
E
Eric Blake 已提交
4355
    bool ret = true;
4356
    const char *name;
4357 4358
    const char *mode = NULL;
    int flags = 0;
4359

4360
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4361
        return false;
4362

4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378
    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
        vshError(ctl, "%s", _("Invalid type"));
        return false;
    }

    if (mode) {
        if (STREQ(mode, "acpi")) {
            flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
        } else if (STREQ(mode, "agent")) {
            flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
        } else {
            vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
            return false;
        }
    }

J
Jim Meyering 已提交
4379
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
4380
        return false;
4381

4382
    if (virDomainReboot(dom, flags) == 0) {
4383
        vshPrint(ctl, _("Domain %s is being rebooted\n"), name);
4384
    } else {
4385
        vshError(ctl, _("Failed to reboot domain %s"), name);
E
Eric Blake 已提交
4386
        ret = false;
4387 4388 4389 4390 4391 4392
    }

    virDomainFree(dom);
    return ret;
}

X
Xu He Jie 已提交
4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430
/*
 * "reset" command
 */
static const vshCmdInfo info_reset[] = {
    {"help", N_("reset a domain")},
    {"desc", N_("Reset the target domain as if by power button")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_reset[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {NULL, 0, 0, NULL}
};

static bool
cmdReset(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    bool ret = true;
    const char *name;

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

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        return false;

    if (virDomainReset(dom, 0) == 0) {
        vshPrint(ctl, _("Domain %s was reset\n"), name);
    } else {
        vshError(ctl, _("Failed to reset domain %s"), name);
        ret = false;
    }

    virDomainFree(dom);
    return ret;
}

4431 4432 4433
/*
 * "destroy" command
 */
4434
static const vshCmdInfo info_destroy[] = {
4435 4436 4437
    {"help", N_("destroy (stop) a domain")},
    {"desc",
     N_("Forcefully stop a given domain, but leave its resources intact.")},
4438
    {NULL, NULL}
4439 4440
};

4441
static const vshCmdOptDef opts_destroy[] = {
4442
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4443
    {"graceful", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("terminate gracefully")},
4444
    {NULL, 0, 0, NULL}
4445 4446
};

E
Eric Blake 已提交
4447
static bool
4448
cmdDestroy(vshControl *ctl, const vshCmd *cmd)
4449
{
4450
    virDomainPtr dom;
E
Eric Blake 已提交
4451
    bool ret = true;
4452
    const char *name;
4453 4454
    unsigned int flags = 0;
    int result;
4455

4456
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4457
        return false;
4458

J
Jim Meyering 已提交
4459
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
4460
        return false;
4461

4462 4463 4464 4465 4466 4467 4468 4469 4470
    if (vshCommandOptBool(cmd, "graceful"))
       flags |= VIR_DOMAIN_DESTROY_GRACEFUL;

    if (flags)
       result = virDomainDestroyFlags(dom, VIR_DOMAIN_DESTROY_GRACEFUL);
    else
       result = virDomainDestroy(dom);

    if (result == 0) {
4471
        vshPrint(ctl, _("Domain %s destroyed\n"), name);
4472
    } else {
4473
        vshError(ctl, _("Failed to destroy domain %s"), name);
E
Eric Blake 已提交
4474
        ret = false;
4475
    }
4476

4477
    virDomainFree(dom);
K
Karel Zak 已提交
4478 4479 4480 4481
    return ret;
}

/*
4482
 * "dominfo" command
K
Karel Zak 已提交
4483
 */
4484
static const vshCmdInfo info_dominfo[] = {
4485 4486
    {"help", N_("domain information")},
    {"desc", N_("Returns basic information about the domain.")},
4487
    {NULL, NULL}
K
Karel Zak 已提交
4488 4489
};

4490
static const vshCmdOptDef opts_dominfo[] = {
4491
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4492
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
4493 4494
};

E
Eric Blake 已提交
4495
static bool
4496
cmdDominfo(vshControl *ctl, const vshCmd *cmd)
4497
{
K
Karel Zak 已提交
4498 4499
    virDomainInfo info;
    virDomainPtr dom;
4500
    virSecurityModel secmodel;
4501
    virSecurityLabelPtr seclabel;
4502
    int persistent = 0;
E
Eric Blake 已提交
4503 4504
    bool ret = true;
    int autostart;
4505
    unsigned int id;
4506
    char *str, uuid[VIR_UUID_STRING_BUFLEN];
4507
    int has_managed_save = 0;
4508

4509
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4510
        return false;
K
Karel Zak 已提交
4511

J
Jim Meyering 已提交
4512
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4513
        return false;
4514

4515 4516
    id = virDomainGetID(dom);
    if (id == ((unsigned int)-1))
4517
        vshPrint(ctl, "%-15s %s\n", _("Id:"), "-");
4518
    else
4519
        vshPrint(ctl, "%-15s %d\n", _("Id:"), id);
4520 4521
    vshPrint(ctl, "%-15s %s\n", _("Name:"), virDomainGetName(dom));

K
Karel Zak 已提交
4522
    if (virDomainGetUUIDString(dom, &uuid[0])==0)
4523
        vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
4524 4525

    if ((str = virDomainGetOSType(dom))) {
4526
        vshPrint(ctl, "%-15s %s\n", _("OS Type:"), str);
4527
        VIR_FREE(str);
4528 4529 4530
    }

    if (virDomainGetInfo(dom, &info) == 0) {
4531
        vshPrint(ctl, "%-15s %s\n", _("State:"),
E
Eric Blake 已提交
4532
                 _(vshDomainStateToString(info.state)));
4533

4534
        vshPrint(ctl, "%-15s %d\n", _("CPU(s):"), info.nrVirtCpu);
4535 4536

        if (info.cpuTime != 0) {
4537
            double cpuUsed = info.cpuTime;
4538

4539
            cpuUsed /= 1000000000.0;
4540

4541
            vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
K
Karel Zak 已提交
4542
        }
4543

4544 4545
        if (info.maxMem != UINT_MAX)
            vshPrint(ctl, "%-15s %lu kB\n", _("Max memory:"),
4546
                 info.maxMem);
4547
        else
4548
            vshPrint(ctl, "%-15s %s\n", _("Max memory:"),
4549 4550
                 _("no limit"));

4551
        vshPrint(ctl, "%-15s %lu kB\n", _("Used memory:"),
4552 4553
                 info.memory);

K
Karel Zak 已提交
4554
    } else {
E
Eric Blake 已提交
4555
        ret = false;
K
Karel Zak 已提交
4556
    }
4557

4558 4559
    /* Check and display whether the domain is persistent or not */
    persistent = virDomainIsPersistent(dom);
4560 4561
    vshDebug(ctl, VSH_ERR_DEBUG, "Domain persistent flag value: %d\n",
             persistent);
4562 4563 4564 4565 4566 4567
    if (persistent < 0)
        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
    else
        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));

    /* Check and display whether the domain autostarts or not */
4568
    if (!virDomainGetAutostart(dom, &autostart)) {
4569
        vshPrint(ctl, "%-15s %s\n", _("Autostart:"),
4570 4571 4572
                 autostart ? _("enable") : _("disable") );
    }

4573 4574 4575 4576 4577 4578 4579
    has_managed_save = virDomainHasManagedSaveImage(dom, 0);
    if (has_managed_save < 0)
        vshPrint(ctl, "%-15s %s\n", _("Managed save:"), _("unknown"));
    else
        vshPrint(ctl, "%-15s %s\n", _("Managed save:"),
                 has_managed_save ? _("yes") : _("no"));

4580 4581 4582
    /* Security model and label information */
    memset(&secmodel, 0, sizeof secmodel);
    if (virNodeGetSecurityModel(ctl->conn, &secmodel) == -1) {
4583 4584
        if (last_error->code != VIR_ERR_NO_SUPPORT) {
            virDomainFree(dom);
E
Eric Blake 已提交
4585
            return false;
4586 4587 4588
        } else {
            virFreeError(last_error);
            last_error = NULL;
4589
        }
4590 4591 4592 4593 4594 4595 4596
    } else {
        /* Only print something if a security model is active */
        if (secmodel.model[0] != '\0') {
            vshPrint(ctl, "%-15s %s\n", _("Security model:"), secmodel.model);
            vshPrint(ctl, "%-15s %s\n", _("Security DOI:"), secmodel.doi);

            /* Security labels are only valid for active domains */
4597
            if (VIR_ALLOC(seclabel) < 0) {
4598
                virDomainFree(dom);
E
Eric Blake 已提交
4599
                return false;
4600 4601 4602 4603 4604
            }

            if (virDomainGetSecurityLabel(dom, seclabel) == -1) {
                virDomainFree(dom);
                VIR_FREE(seclabel);
E
Eric Blake 已提交
4605
                return false;
4606
            } else {
4607
                if (seclabel->label[0] != '\0')
4608
                    vshPrint(ctl, "%-15s %s (%s)\n", _("Security label:"),
4609
                             seclabel->label, seclabel->enforcing ? "enforcing" : "permissive");
4610
            }
4611 4612

            VIR_FREE(seclabel);
4613 4614
        }
    }
4615
    virDomainFree(dom);
K
Karel Zak 已提交
4616 4617 4618
    return ret;
}

4619 4620 4621 4622
/*
 * "domjobinfo" command
 */
static const vshCmdInfo info_domjobinfo[] = {
4623 4624
    {"help", N_("domain job information")},
    {"desc", N_("Returns information about jobs running on a domain.")},
4625 4626 4627 4628
    {NULL, NULL}
};

static const vshCmdOptDef opts_domjobinfo[] = {
4629
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4630 4631 4632 4633
    {NULL, 0, 0, NULL}
};


E
Eric Blake 已提交
4634
static bool
4635 4636 4637 4638
cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
{
    virDomainJobInfo info;
    virDomainPtr dom;
E
Eric Blake 已提交
4639
    bool ret = true;
4640

4641
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4642
        return false;
4643 4644

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4645
        return false;
4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671

    if (virDomainGetJobInfo(dom, &info) == 0) {
        const char *unit;
        double val;

        vshPrint(ctl, "%-17s ", _("Job type:"));
        switch (info.type) {
        case VIR_DOMAIN_JOB_BOUNDED:
            vshPrint(ctl, "%-12s\n", _("Bounded"));
            break;

        case VIR_DOMAIN_JOB_UNBOUNDED:
            vshPrint(ctl, "%-12s\n", _("Unbounded"));
            break;

        case VIR_DOMAIN_JOB_NONE:
        default:
            vshPrint(ctl, "%-12s\n", _("None"));
            goto cleanup;
        }

        vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed);
        if (info.type == VIR_DOMAIN_JOB_BOUNDED)
            vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining);
        if (info.dataTotal || info.dataRemaining || info.dataProcessed) {
            val = prettyCapacity(info.dataProcessed, &unit);
E
Eric Blake 已提交
4672
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
4673
            val = prettyCapacity(info.dataRemaining, &unit);
E
Eric Blake 已提交
4674
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
4675
            val = prettyCapacity(info.dataTotal, &unit);
E
Eric Blake 已提交
4676
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
4677 4678 4679
        }
        if (info.memTotal || info.memRemaining || info.memProcessed) {
            val = prettyCapacity(info.memProcessed, &unit);
E
Eric Blake 已提交
4680
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
4681
            val = prettyCapacity(info.memRemaining, &unit);
E
Eric Blake 已提交
4682
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
4683
            val = prettyCapacity(info.memTotal, &unit);
E
Eric Blake 已提交
4684
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
4685 4686 4687
        }
        if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
            val = prettyCapacity(info.fileProcessed, &unit);
E
Eric Blake 已提交
4688
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
4689
            val = prettyCapacity(info.fileRemaining, &unit);
E
Eric Blake 已提交
4690
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
4691
            val = prettyCapacity(info.fileTotal, &unit);
E
Eric Blake 已提交
4692
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
4693 4694
        }
    } else {
E
Eric Blake 已提交
4695
        ret = false;
4696 4697 4698 4699 4700 4701
    }
cleanup:
    virDomainFree(dom);
    return ret;
}

4702 4703 4704 4705
/*
 * "domjobabort" command
 */
static const vshCmdInfo info_domjobabort[] = {
4706 4707
    {"help", N_("abort active domain job")},
    {"desc", N_("Aborts the currently running domain job")},
4708 4709 4710 4711
    {NULL, NULL}
};

static const vshCmdOptDef opts_domjobabort[] = {
4712
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4713 4714 4715
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4716
static bool
4717 4718 4719
cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
4720
    bool ret = true;
4721

4722
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4723
        return false;
4724 4725

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4726
        return false;
4727 4728

    if (virDomainAbortJob(dom) < 0)
E
Eric Blake 已提交
4729
        ret = false;
4730 4731 4732 4733 4734

    virDomainFree(dom);
    return ret;
}

4735 4736 4737
/*
 * "freecell" command
 */
4738
static const vshCmdInfo info_freecell[] = {
4739 4740
    {"help", N_("NUMA free memory")},
    {"desc", N_("display available free memory for the NUMA cell.")},
4741 4742 4743
    {NULL, NULL}
};

4744
static const vshCmdOptDef opts_freecell[] = {
4745
    {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
4746
    {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
4747 4748 4749
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4750
static bool
4751
cmdFreecell(vshControl *ctl, const vshCmd *cmd)
4752
{
W
Wen Congyang 已提交
4753
    bool func_ret = false;
4754
    int ret;
4755
    int cell = -1, cell_given;
4756
    unsigned long long memory;
4757 4758 4759 4760
    xmlNodePtr *nodes = NULL;
    unsigned long nodes_cnt;
    unsigned long *nodes_id = NULL;
    unsigned long long *nodes_free = NULL;
4761
    int all_given;
4762 4763 4764 4765
    int i;
    char *cap_xml = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
4766

4767

4768
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4769
        return false;
4770

4771 4772 4773 4774
    if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
        vshError(ctl, "%s", _("cell number has to be a number"));
        goto cleanup;
    }
4775 4776 4777 4778 4779 4780 4781 4782 4783
    all_given = vshCommandOptBool(cmd, "all");

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

    if (all_given) {
4784 4785 4786
        cap_xml = virConnectGetCapabilities(ctl->conn);
        if (!cap_xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
4787 4788 4789
            goto cleanup;
        }

4790
        xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
4791 4792
        if (!xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
4793 4794
            goto cleanup;
        }
4795 4796 4797 4798
        nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
                                    ctxt, &nodes);

        if (nodes_cnt == -1) {
4799
            vshError(ctl, "%s", _("could not get information about "
4800
                                  "NUMA topology"));
4801 4802 4803
            goto cleanup;
        }

4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824
        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;
            }
        }

4825
        memory = 0;
4826 4827 4828 4829
        for (cell = 0; cell < nodes_cnt; cell++) {
            vshPrint(ctl, "%5lu: %10llu kB\n", nodes_id[cell],
                    (nodes_free[cell]/1024));
            memory += nodes_free[cell];
4830 4831 4832 4833
        }

        vshPrintExtra(ctl, "--------------------\n");
        vshPrintExtra(ctl, "%5s: %10llu kB\n", _("Total"), memory/1024);
4834
    } else {
4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848
        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 kB\n", _("Total"), (memory/1024));
        else
            vshPrint(ctl, "%d: %llu kB\n", cell, (memory/1024));
4849 4850
    }

E
Eric Blake 已提交
4851
    func_ret = true;
4852

4853
cleanup:
4854
    xmlXPathFreeContext(ctxt);
4855
    xmlFreeDoc(xml);
4856
    VIR_FREE(nodes);
4857 4858 4859
    VIR_FREE(nodes_free);
    VIR_FREE(nodes_id);
    VIR_FREE(cap_xml);
4860
    return func_ret;
4861 4862
}

E
Eric Blake 已提交
4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876
/*
 * "maxvcpus" command
 */
static const vshCmdInfo info_maxvcpus[] = {
    {"help", N_("connection vcpu maximum")},
    {"desc", N_("Show maximum number of virtual CPUs for guests on this connection.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_maxvcpus[] = {
    {"type", VSH_OT_STRING, 0, N_("domain type")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4877
static bool
E
Eric Blake 已提交
4878 4879
cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
{
4880
    const char *type = NULL;
E
Eric Blake 已提交
4881 4882
    int vcpus;

4883 4884
    if (vshCommandOptString(cmd, "type", &type) < 0) {
        vshError(ctl, "%s", _("Invalid type"));
E
Eric Blake 已提交
4885
        return false;
4886
    }
E
Eric Blake 已提交
4887 4888

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4889
        return false;
E
Eric Blake 已提交
4890 4891 4892

    vcpus = virConnectGetMaxVcpus(ctl->conn, type);
    if (vcpus < 0)
E
Eric Blake 已提交
4893
        return false;
E
Eric Blake 已提交
4894 4895
    vshPrint(ctl, "%d\n", vcpus);

E
Eric Blake 已提交
4896
    return true;
E
Eric Blake 已提交
4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910
}

/*
 * "vcpucount" command
 */
static const vshCmdInfo info_vcpucount[] = {
    {"help", N_("domain vcpu counts")},
    {"desc", N_("Returns the number of virtual CPUs used by the domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_vcpucount[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"maximum", VSH_OT_BOOL, 0, N_("get maximum cap on vcpus")},
4911
    {"active", VSH_OT_BOOL, 0, N_("get number of currently active vcpus")},
E
Eric Blake 已提交
4912
    {"live", VSH_OT_BOOL, 0, N_("get value from running domain")},
4913 4914 4915
    {"config", VSH_OT_BOOL, 0, N_("get value to be used on next boot")},
    {"current", VSH_OT_BOOL, 0,
     N_("get value according to current domain state")},
E
Eric Blake 已提交
4916 4917 4918
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4919
static bool
E
Eric Blake 已提交
4920 4921 4922
cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
4923
    bool ret = true;
E
Eric Blake 已提交
4924
    int maximum = vshCommandOptBool(cmd, "maximum");
4925
    int active = vshCommandOptBool(cmd, "active");
E
Eric Blake 已提交
4926 4927
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
4928 4929
    int current = vshCommandOptBool(cmd, "current");
    bool all = maximum + active + current + config + live == 0;
E
Eric Blake 已提交
4930 4931
    int count;

4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946
    /* We want one of each pair of mutually exclusive options; that
     * is, use of flags requires exactly two options.  We reject the
     * use of more than 2 flags later on.  */
    if (maximum + active + current + config + live == 1) {
        if (maximum || active) {
            vshError(ctl,
                     _("when using --%s, one of --config, --live, or --current "
                       "must be specified"),
                     maximum ? "maximum" : "active");
        } else {
            vshError(ctl,
                     _("when using --%s, either --maximum or --active must be "
                       "specified"),
                     (current ? "current" : config ? "config" : "live"));
        }
E
Eric Blake 已提交
4947
        return false;
E
Eric Blake 已提交
4948
    }
4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961

    /* Backwards compatibility: prior to 0.9.4,
     * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant
     * the opposite of --maximum.  Translate the old '--current
     * --live' into the new '--active --live', while treating the new
     * '--maximum --current' correctly rather than rejecting it as
     * '--maximum --active'.  */
    if (!maximum && !active && current) {
        current = false;
        active = true;
    }

    if (maximum && active) {
E
Eric Blake 已提交
4962
        vshError(ctl, "%s",
4963
                 _("--maximum and --active cannot both be specified"));
E
Eric Blake 已提交
4964
        return false;
E
Eric Blake 已提交
4965
    }
4966 4967 4968
    if (current + config + live > 1) {
        vshError(ctl, "%s",
                 _("--config, --live, and --current are mutually exclusive"));
E
Eric Blake 已提交
4969
        return false;
E
Eric Blake 已提交
4970 4971 4972
    }

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4973
        return false;
E
Eric Blake 已提交
4974 4975

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4976
        return false;
E
Eric Blake 已提交
4977 4978

    /* In all cases, try the new API first; if it fails because we are
4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992
     * talking to an older client, generally we try a fallback API
     * before giving up.  --current requires the new API, since we
     * don't know whether the domain is running or inactive.  */
    if (current) {
        count = virDomainGetVcpusFlags(dom,
                                       maximum ? VIR_DOMAIN_VCPU_MAXIMUM : 0);
        if (count < 0) {
            virshReportError(ctl);
            ret = false;
        } else {
            vshPrint(ctl, "%d\n", count);
        }
    }

E
Eric Blake 已提交
4993 4994
    if (all || (maximum && config)) {
        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
4995
                                             VIR_DOMAIN_AFFECT_CONFIG));
E
Eric Blake 已提交
4996 4997 4998 4999 5000 5001 5002 5003 5004
        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
                          || last_error->code == VIR_ERR_INVALID_ARG)) {
            char *tmp;
            char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
            if (xml && (tmp = strstr(xml, "<vcpu"))) {
                tmp = strchr(tmp, '>');
                if (!tmp || virStrToLong_i(tmp + 1, &tmp, 10, &count) < 0)
                    count = -1;
            }
5005 5006
            virFreeError(last_error);
            last_error = NULL;
E
Eric Blake 已提交
5007 5008 5009 5010 5011
            VIR_FREE(xml);
        }

        if (count < 0) {
            virshReportError(ctl);
E
Eric Blake 已提交
5012
            ret = false;
E
Eric Blake 已提交
5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("config"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

    if (all || (maximum && live)) {
        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
5025
                                             VIR_DOMAIN_AFFECT_LIVE));
E
Eric Blake 已提交
5026 5027 5028 5029 5030 5031 5032
        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
                          || last_error->code == VIR_ERR_INVALID_ARG)) {
            count = virDomainGetMaxVcpus(dom);
        }

        if (count < 0) {
            virshReportError(ctl);
E
Eric Blake 已提交
5033
            ret = false;
E
Eric Blake 已提交
5034 5035 5036 5037 5038 5039 5040 5041 5042 5043
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

5044
    if (all || (active && config)) {
5045
        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_CONFIG);
E
Eric Blake 已提交
5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069
        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
                          || last_error->code == VIR_ERR_INVALID_ARG)) {
            char *tmp, *end;
            char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
            if (xml && (tmp = strstr(xml, "<vcpu"))) {
                end = strchr(tmp, '>');
                if (end) {
                    *end = '\0';
                    tmp = strstr(tmp, "current=");
                    if (!tmp)
                        tmp = end + 1;
                    else {
                        tmp += strlen("current=");
                        tmp += *tmp == '\'' || *tmp == '"';
                    }
                }
                if (!tmp || virStrToLong_i(tmp, &tmp, 10, &count) < 0)
                    count = -1;
            }
            VIR_FREE(xml);
        }

        if (count < 0) {
            virshReportError(ctl);
E
Eric Blake 已提交
5070
            ret = false;
E
Eric Blake 已提交
5071 5072 5073 5074 5075 5076 5077 5078 5079 5080
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

5081
    if (all || (active && live)) {
5082
        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_LIVE);
E
Eric Blake 已提交
5083 5084 5085 5086 5087 5088 5089 5090 5091
        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
                          || last_error->code == VIR_ERR_INVALID_ARG)) {
            virDomainInfo info;
            if (virDomainGetInfo(dom, &info) == 0)
                count = info.nrVirtCpu;
        }

        if (count < 0) {
            virshReportError(ctl);
E
Eric Blake 已提交
5092
            ret = false;
E
Eric Blake 已提交
5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("live"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

    virDomainFree(dom);
    return ret;
}

5107 5108 5109
/*
 * "vcpuinfo" command
 */
5110
static const vshCmdInfo info_vcpuinfo[] = {
E
Eric Blake 已提交
5111
    {"help", N_("detailed domain vcpu information")},
5112
    {"desc", N_("Returns basic information about the domain virtual CPUs.")},
5113 5114 5115
    {NULL, NULL}
};

5116
static const vshCmdOptDef opts_vcpuinfo[] = {
5117
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5118 5119 5120
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5121
static bool
5122
cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
5123 5124 5125 5126 5127
{
    virDomainInfo info;
    virDomainPtr dom;
    virNodeInfo nodeinfo;
    virVcpuInfoPtr cpuinfo;
5128 5129
    unsigned char *cpumaps;
    int ncpus, maxcpu;
5130
    size_t cpumaplen;
E
Eric Blake 已提交
5131
    bool ret = true;
5132
    int n, m;
5133

5134
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5135
        return false;
5136

J
Jim Meyering 已提交
5137
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5138
        return false;
5139 5140 5141

    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
5142
        return false;
5143 5144 5145 5146
    }

    if (virDomainGetInfo(dom, &info) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
5147
        return false;
5148 5149
    }

5150
    cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
5151 5152 5153
    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
    cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
5154

5155 5156
    if ((ncpus = virDomainGetVcpus(dom,
                                   cpuinfo, info.nrVirtCpu,
5157
                                   cpumaps, cpumaplen)) >= 0) {
5158 5159 5160 5161
        for (n = 0 ; n < ncpus ; n++) {
            vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
            vshPrint(ctl, "%-15s %d\n", _("CPU:"), cpuinfo[n].cpu);
            vshPrint(ctl, "%-15s %s\n", _("State:"),
E
Eric Blake 已提交
5162
                     _(vshDomainVcpuStateToString(cpuinfo[n].state)));
5163 5164 5165 5166 5167 5168 5169 5170
            if (cpuinfo[n].cpuTime != 0) {
                double cpuUsed = cpuinfo[n].cpuTime;

                cpuUsed /= 1000000000.0;

                vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
            }
            vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
5171 5172
            for (m = 0; m < maxcpu; m++) {
                vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
5173 5174 5175 5176 5177 5178
            }
            vshPrint(ctl, "\n");
            if (n < (ncpus - 1)) {
                vshPrint(ctl, "\n");
            }
        }
5179
    } else {
5180
        if (info.state == VIR_DOMAIN_SHUTOFF &&
E
Eric Blake 已提交
5181
            (ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
5182 5183 5184
                                             cpumaps, cpumaplen,
                                             VIR_DOMAIN_AFFECT_CONFIG)) >= 0) {

E
Eric Blake 已提交
5185
            /* fallback plan to use virDomainGetVcpuPinInfo */
5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203

            for (n = 0; n < ncpus; n++) {
                vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
                vshPrint(ctl, "%-15s %s\n", _("CPU:"), _("N/A"));
                vshPrint(ctl, "%-15s %s\n", _("State:"), _("N/A"));
                vshPrint(ctl, "%-15s %s\n", _("CPU time"), _("N/A"));
                vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
                for (m = 0; m < maxcpu; m++) {
                    vshPrint(ctl, "%c",
                             VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
                }
                vshPrint(ctl, "\n");
                if (n < (ncpus - 1)) {
                    vshPrint(ctl, "\n");
                }
            }
        } else {
            ret = false;
5204
        }
5205 5206
    }

5207
    VIR_FREE(cpumaps);
5208
    VIR_FREE(cpuinfo);
5209 5210 5211 5212 5213 5214 5215
    virDomainFree(dom);
    return ret;
}

/*
 * "vcpupin" command
 */
5216
static const vshCmdInfo info_vcpupin[] = {
5217
    {"help", N_("control or query domain vcpu affinity")},
5218
    {"desc", N_("Pin domain VCPUs to host physical CPUs.")},
5219 5220 5221
    {NULL, NULL}
};

5222
static const vshCmdOptDef opts_vcpupin[] = {
5223
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5224 5225 5226
    {"vcpu", VSH_OT_INT, 0, N_("vcpu number")},
    {"cpulist", VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
     N_("host cpu number(s) to set, or omit option to query")},
5227 5228 5229
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
5230 5231 5232
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5233
static bool
E
Eric Blake 已提交
5234
cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
5235 5236 5237 5238
{
    virDomainInfo info;
    virDomainPtr dom;
    virNodeInfo nodeinfo;
5239
    int vcpu = -1;
5240
    const char *cpulist = NULL;
E
Eric Blake 已提交
5241
    bool ret = true;
5242 5243
    unsigned char *cpumap = NULL;
    unsigned char *cpumaps = NULL;
E
Eric Blake 已提交
5244
    size_t cpumaplen;
5245 5246
    bool bit, lastbit, isInvert;
    int i, cpu, lastcpu, maxcpu, ncpus;
5247
    bool unuse = false;
5248
    const char *cur;
5249 5250 5251
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    int current = vshCommandOptBool(cmd, "current");
5252
    bool query = false; /* Query mode if no cpulist */
E
Eric Blake 已提交
5253
    unsigned int flags = 0;
5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
        /* neither option is specified */
        if (!live && !config)
            flags = -1;
    }
5270

5271
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5272
        return false;
5273

J
Jim Meyering 已提交
5274
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5275
        return false;
5276

5277 5278
    if (vshCommandOptString(cmd, "cpulist", &cpulist) < 0) {
        vshError(ctl, "%s", _("vcpupin: Missing cpulist."));
5279
        virDomainFree(dom);
E
Eric Blake 已提交
5280
        return false;
5281
    }
5282
    query = !cpulist;
5283

5284 5285 5286 5287
    /* In query mode, "vcpu" is optional */
    if (vshCommandOptInt(cmd, "vcpu", &vcpu) < !query) {
        vshError(ctl, "%s",
                 _("vcpupin: Invalid or missing vCPU number."));
5288
        virDomainFree(dom);
E
Eric Blake 已提交
5289
        return false;
5290
    }
5291

5292 5293
    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
5294
        return false;
5295 5296 5297
    }

    if (virDomainGetInfo(dom, &info) != 0) {
5298
        vshError(ctl, "%s", _("vcpupin: failed to get domain information."));
5299
        virDomainFree(dom);
E
Eric Blake 已提交
5300
        return false;
5301 5302 5303
    }

    if (vcpu >= info.nrVirtCpu) {
5304
        vshError(ctl, "%s", _("vcpupin: Invalid vCPU number."));
5305
        virDomainFree(dom);
E
Eric Blake 已提交
5306
        return false;
5307 5308
    }

5309 5310
    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
5311

5312 5313 5314 5315 5316 5317 5318 5319
    /* Query mode: show CPU affinity information then exit.*/
    if (query) {
        /* When query mode and neither "live", "config" nor "current"
         * is specified, set VIR_DOMAIN_AFFECT_CURRENT as flags */
        if (flags == -1)
            flags = VIR_DOMAIN_AFFECT_CURRENT;

        cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
E
Eric Blake 已提交
5320
        if ((ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364
                                             cpumaps, cpumaplen, flags)) >= 0) {

            vshPrint(ctl, "%s %s\n", _("VCPU:"), _("CPU Affinity"));
            vshPrint(ctl, "----------------------------------\n");
            for (i = 0; i < ncpus; i++) {

               if (vcpu != -1 && i != vcpu)
                   continue;

               bit = lastbit = isInvert = false;
               lastcpu = -1;

               vshPrint(ctl, "%4d: ", i);
               for (cpu = 0; cpu < maxcpu; cpu++) {

                   bit = VIR_CPU_USABLE(cpumaps, cpumaplen, i, cpu);

                   isInvert = (bit ^ lastbit);
                   if (bit && isInvert) {
                       if (lastcpu == -1)
                           vshPrint(ctl, "%d", cpu);
                       else
                           vshPrint(ctl, ",%d", cpu);
                       lastcpu = cpu;
                   }
                   if (!bit && isInvert && lastcpu != cpu - 1)
                       vshPrint(ctl, "-%d", cpu - 1);
                   lastbit = bit;
               }
               if (bit && !isInvert) {
                  vshPrint(ctl, "-%d", maxcpu - 1);
               }
               vshPrint(ctl, "\n");
            }

        } else {
            ret = false;
        }
        VIR_FREE(cpumaps);
        goto cleanup;
    }

    /* Pin mode: pinning specified vcpu to specified physical cpus*/

5365
    cpumap = vshCalloc(ctl, cpumaplen, sizeof(cpumap));
5366 5367
    /* Parse cpulist */
    cur = cpulist;
5368
    if (*cur == 0) {
5369
        goto parse_error;
5370 5371 5372
    } else if (*cur == 'r') {
        for (cpu = 0; cpu < maxcpu; cpu++)
            VIR_USE_CPU(cpumap, cpu);
5373
        cur = "";
5374
    }
5375 5376 5377 5378 5379 5380 5381

    while (*cur != 0) {

        /* the char '^' denotes exclusive */
        if (*cur == '^') {
            cur++;
            unuse = true;
5382 5383
        }

5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395
        /* parse physical CPU number */
        if (!c_isdigit(*cur))
            goto parse_error;
        cpu  = virParseNumber(&cur);
        if (cpu < 0) {
            goto parse_error;
        }
        if (cpu >= maxcpu) {
            vshError(ctl, _("Physical CPU %d doesn't exist."), cpu);
            goto parse_error;
        }
        virSkipSpaces(&cur);
5396

5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423
        if ((*cur == ',') || (*cur == 0)) {
            if (unuse) {
                VIR_UNUSE_CPU(cpumap, cpu);
            } else {
                VIR_USE_CPU(cpumap, cpu);
            }
        } else if (*cur == '-') {
            /* the char '-' denotes range */
            if (unuse) {
                goto parse_error;
            }
            cur++;
            virSkipSpaces(&cur);
            /* parse the end of range */
            lastcpu = virParseNumber(&cur);
            if (lastcpu < cpu) {
                goto parse_error;
            }
            if (lastcpu >= maxcpu) {
                vshError(ctl, _("Physical CPU %d doesn't exist."), maxcpu);
                goto parse_error;
            }
            for (i = cpu; i <= lastcpu; i++) {
                VIR_USE_CPU(cpumap, i);
            }
            virSkipSpaces(&cur);
        }
5424

5425 5426 5427 5428 5429 5430
        if (*cur == ',') {
            cur++;
            virSkipSpaces(&cur);
            unuse = false;
        } else if (*cur == 0) {
            break;
5431
        } else {
5432
            goto parse_error;
5433
        }
5434
    }
5435

5436 5437 5438 5439 5440 5441 5442 5443
    if (flags == -1) {
        if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) {
            ret = false;
        }
    } else {
        if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0) {
            ret = false;
        }
5444 5445
    }

5446
cleanup:
5447
    VIR_FREE(cpumap);
5448 5449
    virDomainFree(dom);
    return ret;
5450 5451 5452 5453 5454

parse_error:
    vshError(ctl, "%s", _("cpulist: Invalid format."));
    ret = false;
    goto cleanup;
5455 5456
}

5457 5458 5459
/*
 * "setvcpus" command
 */
5460
static const vshCmdInfo info_setvcpus[] = {
5461 5462
    {"help", N_("change number of virtual CPUs")},
    {"desc", N_("Change the number of virtual CPUs in the guest domain.")},
5463 5464 5465
    {NULL, NULL}
};

5466
static const vshCmdOptDef opts_setvcpus[] = {
5467
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5468
    {"count", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of virtual CPUs")},
E
Eric Blake 已提交
5469 5470 5471
    {"maximum", VSH_OT_BOOL, 0, N_("set maximum limit on next boot")},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
5472
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
5473 5474 5475
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5476
static bool
5477
cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
5478 5479
{
    virDomainPtr dom;
5480
    int count = 0;
E
Eric Blake 已提交
5481
    bool ret = true;
E
Eric Blake 已提交
5482 5483 5484
    int maximum = vshCommandOptBool(cmd, "maximum");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
5485
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
5486
    unsigned int flags = 0;
5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
        /* neither option is specified */
        if (!live && !config && !maximum)
            flags = -1;
    }
5503

5504
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5505
        return false;
5506

J
Jim Meyering 已提交
5507
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5508
        return false;
5509

5510 5511 5512 5513
    if (vshCommandOptInt(cmd, "count", &count) < 0) {
        vshError(ctl, "%s", _("Invalid number of virtual CPUs"));
        goto cleanup;
    }
5514

5515
    if (flags == -1) {
E
Eric Blake 已提交
5516
        if (virDomainSetVcpus(dom, count) != 0) {
E
Eric Blake 已提交
5517
            ret = false;
E
Eric Blake 已提交
5518 5519
        }
    } else {
5520 5521 5522
        /* If the --maximum flag was given, we need to ensure only the
           --config flag is in effect as well */
        if (maximum) {
5523
            vshDebug(ctl, VSH_ERR_DEBUG, "--maximum flag was given\n");
5524

5525 5526
            flags |= VIR_DOMAIN_VCPU_MAXIMUM;

5527 5528 5529 5530 5531 5532 5533 5534
            /* If neither the --config nor --live flags were given, OR
               if just the --live flag was given, we need to error out
               warning the user that the --maximum flag can only be used
               with the --config flag */
            if (live || !config) {

                /* Warn the user about the invalid flag combination */
                vshError(ctl, _("--maximum must be used with --config only"));
E
Eric Blake 已提交
5535
                ret = false;
5536 5537 5538 5539 5540
                goto cleanup;
            }
        }

        /* Apply the virtual cpu changes */
E
Eric Blake 已提交
5541
        if (virDomainSetVcpusFlags(dom, count, flags) < 0) {
E
Eric Blake 已提交
5542
            ret = false;
E
Eric Blake 已提交
5543
        }
5544 5545
    }

5546
  cleanup:
5547 5548 5549 5550
    virDomainFree(dom);
    return ret;
}

5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632
/*
 * "cpu-stats" command
 */
static const vshCmdInfo info_cpu_stats[] = {
    {"help", N_("show domain cpu statistics")},
    {"desc",
     N_("Display per-CPU and total statistics about the domain's CPUs")},
    {NULL, NULL},
};

static const vshCmdOptDef opts_cpu_stats[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"total", VSH_OT_BOOL, 0, N_("Show total statistics only")},
    {"start", VSH_OT_INT, 0, N_("Show statistics from this CPU")},
    {"count", VSH_OT_INT, 0, N_("Number of shown CPUs at most")},
    {NULL, 0, 0, NULL},
};

static bool
cmdCPUStats(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    virTypedParameterPtr params = NULL;
    int i, j, pos, max_id, cpu = -1, show_count = -1, nparams;
    bool show_total = false, show_per_cpu = false;

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

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

    show_total = vshCommandOptBool(cmd, "total");
    if (vshCommandOptInt(cmd, "start", &cpu) > 0)
        show_per_cpu = true;
    if (vshCommandOptInt(cmd, "count", &show_count) > 0)
        show_per_cpu = true;

    /* default show per_cpu and total */
    if (!show_total && !show_per_cpu) {
        show_total = true;
        show_per_cpu = true;
    }

    if (!show_per_cpu) /* show total stats only */
        goto do_show_total;

    /* check cpu, show_count, and ignore wrong argument */
    if (cpu < 0)
        cpu = 0;

    /* get number of cpus on the node */
    if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0)) < 0)
        goto failed_stats;
    if (show_count < 0 || show_count > max_id)
        show_count = max_id;

    /* get percpu information */
    if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, 0)) < 0)
        goto failed_stats;

    if (!nparams) {
        vshPrint(ctl, "%s", _("No per-CPU stats available"));
        goto do_show_total;
    }

    if (VIR_ALLOC_N(params, nparams * MIN(show_count, 128)) < 0)
        goto failed_params;

    while (show_count) {
        int ncpus = MIN(show_count, 128);

        if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, 0) < 0)
            goto failed_stats;

        for (i = 0; i < ncpus; i++) {
            if (params[i * nparams].type == 0) /* this cpu is not in the map */
                continue;
            vshPrint(ctl, "CPU%d:\n", cpu + i);

            for (j = 0; j < nparams; j++) {
                pos = i * nparams + j;
E
Eric Blake 已提交
5633
                vshPrint(ctl, "\t%-12s ", params[pos].field);
5634 5635
                if (STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME) &&
                    params[j].type == VIR_TYPED_PARAM_ULLONG) {
E
Eric Blake 已提交
5636
                    vshPrint(ctl, "%9lld.%09lld seconds\n",
5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673
                             params[pos].value.ul / 1000000000,
                             params[pos].value.ul % 1000000000);
                } else {
                    const char *s = vshGetTypedParamValue(ctl, &params[pos]);
                    vshPrint(ctl, _("%s\n"), s);
                    VIR_FREE(s);
                }
            }
        }
        cpu += ncpus;
        show_count -= ncpus;
        virTypedParameterArrayClear(params, nparams * ncpus);
    }
    VIR_FREE(params);

do_show_total:
    if (!show_total)
        goto cleanup;

    /* get supported num of parameter for total statistics */
    if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0)) < 0)
        goto failed_stats;

    if (!nparams) {
        vshPrint(ctl, "%s", _("No total stats available"));
        goto cleanup;
    }

    if (VIR_ALLOC_N(params, nparams))
        goto failed_params;

    /* passing start_cpu == -1 gives us domain's total status */
    if ((nparams = virDomainGetCPUStats(dom, params, nparams, -1, 1, 0)) < 0)
        goto failed_stats;

    vshPrint(ctl, _("Total:\n"));
    for (i = 0; i < nparams; i++) {
E
Eric Blake 已提交
5674 5675 5676 5677
        vshPrint(ctl, "\t%-12s ", params[i].field);
        if ((STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
             STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_USERTIME) ||
             STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_SYSTEMTIME)) &&
5678
            params[i].type == VIR_TYPED_PARAM_ULLONG) {
E
Eric Blake 已提交
5679
            vshPrint(ctl, "%9lld.%09lld seconds\n",
5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706
                     params[i].value.ul / 1000000000,
                     params[i].value.ul % 1000000000);
        } else {
            char *s = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%s\n", s);
            VIR_FREE(s);
        }
    }
    virTypedParameterArrayClear(params, nparams);
    VIR_FREE(params);

cleanup:
    virDomainFree(dom);
    return true;

failed_params:
    virReportOOMError();
    virDomainFree(dom);
    return false;

failed_stats:
    vshError(ctl, _("Failed to virDomainGetCPUStats()\n"));
    VIR_FREE(params);
    virDomainFree(dom);
    return false;
}

5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740
/*
 * "inject-nmi" command
 */
static const vshCmdInfo info_inject_nmi[] = {
    {"help", N_("Inject NMI to the guest")},
    {"desc", N_("Inject NMI to the guest domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_inject_nmi[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {NULL, 0, 0, NULL}
};


static bool
cmdInjectNMI(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    int ret = true;

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

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

    if (virDomainInjectNMI(dom, 0) < 0)
            ret = false;

    virDomainFree(dom);
    return ret;
}

5741 5742 5743 5744 5745
/*
 * "send-key" command
 */
static const vshCmdInfo info_send_key[] = {
    {"help", N_("Send keycodes to the guest")},
5746
    {"desc", N_("Send keycodes (integers or symbolic names) to the guest")},
5747 5748 5749 5750 5751
    {NULL, NULL}
};

static const vshCmdOptDef opts_send_key[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5752 5753
    {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT,
     N_("the codeset of keycodes, default:linux")},
5754
    {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT,
A
Alex Jia 已提交
5755
     N_("the time (in milliseconds) how long the keys will be held")},
5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827
    {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")},
    {NULL, 0, 0, NULL}
};

static int get_integer_keycode(const char *key_name)
{
    long val;
    char *endptr;

    val = strtol(key_name, &endptr, 0);
    if (*endptr != '\0' || val > 0xffff || val <= 0)
         return -1;

    return val;
}

static bool
cmdSendKey(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    int ret = false;
    const char *codeset_option;
    int codeset;
    int holdtime;
    int count = 0;
    const vshCmdOpt *opt = NULL;
    int keycode;
    unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];

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

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

    if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0)
        codeset_option = "linux";

    if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0)
        holdtime = 0;

    codeset = virKeycodeSetTypeFromString(codeset_option);
    if ((int)codeset < 0) {
        vshError(ctl, _("unknown codeset: '%s'"), codeset_option);
        goto cleanup;
    }

    while ((opt = vshCommandOptArgv(cmd, opt))) {
        if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
            vshError(ctl, _("too many keycodes"));
            goto cleanup;
        }

        if ((keycode = get_integer_keycode(opt->data)) <= 0) {
            if ((keycode = virKeycodeValueFromString(codeset, opt->data)) <= 0) {
                vshError(ctl, _("invalid keycode: '%s'"), opt->data);
                goto cleanup;
            }
        }

        keycodes[count] = keycode;
        count++;
    }

    if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0))
        ret = true;

cleanup:
    virDomainFree(dom);
    return ret;
}

5828
/*
E
Eric Blake 已提交
5829
 * "setmem" command
5830
 */
5831
static const vshCmdInfo info_setmem[] = {
5832 5833
    {"help", N_("change memory allocation")},
    {"desc", N_("Change the current memory allocation in the guest domain.")},
5834 5835 5836
    {NULL, NULL}
};

5837
static const vshCmdOptDef opts_setmem[] = {
5838
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
E
Eric Blake 已提交
5839 5840 5841
    {"kilobytes", VSH_OT_ALIAS, 0, "size"},
    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
     N_("new memory size, as scaled integer (default KiB)")},
5842 5843
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
5844
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
5845 5846 5847
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5848
static bool
5849
cmdSetmem(vshControl *ctl, const vshCmd *cmd)
5850 5851
{
    virDomainPtr dom;
E
Eric Blake 已提交
5852 5853 5854
    unsigned long long bytes = 0;
    unsigned long long max;
    unsigned long kibibytes = 0;
E
Eric Blake 已提交
5855
    bool ret = true;
5856 5857
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
5858
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
5859
    unsigned int flags = 0;
5860

5861 5862 5863
    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
E
Eric Blake 已提交
5864
            return false;
5865
        }
5866
        flags = VIR_DOMAIN_AFFECT_CURRENT;
5867 5868
    } else {
        if (config)
5869
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
5870
        if (live)
5871
            flags |= VIR_DOMAIN_AFFECT_LIVE;
5872 5873 5874
        /* neither option is specified */
        if (!live && !config)
            flags = -1;
5875
    }
5876

5877
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5878
        return false;
5879

J
Jim Meyering 已提交
5880
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5881
        return false;
5882

E
Eric Blake 已提交
5883 5884 5885 5886 5887 5888 5889
    /* The API expects 'unsigned long' KiB, so depending on whether we
     * are 32-bit or 64-bit determines the maximum we can use.  */
    if (sizeof(kibibytes) < sizeof(max))
        max = 1024ull * ULONG_MAX;
    else
        max = ULONG_MAX;
    if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
5890
        vshError(ctl, "%s", _("memory size has to be a number"));
5891
        virDomainFree(dom);
E
Eric Blake 已提交
5892
        return false;
5893
    }
E
Eric Blake 已提交
5894
    kibibytes = VIR_DIV_UP(bytes, 1024);
5895

5896
    if (flags == -1) {
E
Eric Blake 已提交
5897
        if (virDomainSetMemory(dom, kibibytes) != 0) {
E
Eric Blake 已提交
5898
            ret = false;
5899 5900
        }
    } else {
E
Eric Blake 已提交
5901
        if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
E
Eric Blake 已提交
5902
            ret = false;
5903
        }
5904 5905 5906 5907 5908 5909 5910 5911 5912
    }

    virDomainFree(dom);
    return ret;
}

/*
 * "setmaxmem" command
 */
5913
static const vshCmdInfo info_setmaxmem[] = {
5914 5915
    {"help", N_("change maximum memory limit")},
    {"desc", N_("Change the maximum memory allocation limit in the guest domain.")},
5916 5917 5918
    {NULL, NULL}
};

5919
static const vshCmdOptDef opts_setmaxmem[] = {
5920
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
E
Eric Blake 已提交
5921 5922 5923
    {"kilobytes", VSH_OT_ALIAS, 0, "size"},
    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
     N_("new maximum memory size, as scaled integer (default KiB)")},
5924 5925 5926
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
5927 5928 5929
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5930
static bool
5931
cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
5932 5933
{
    virDomainPtr dom;
E
Eric Blake 已提交
5934 5935 5936
    unsigned long long bytes = 0;
    unsigned long long max;
    unsigned long kibibytes = 0;
E
Eric Blake 已提交
5937
    bool ret = true;
5938 5939 5940
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
5941
    unsigned int flags = VIR_DOMAIN_MEM_MAXIMUM;
5942 5943 5944 5945

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
E
Eric Blake 已提交
5946
            return false;
5947 5948 5949
        }
    } else {
        if (config)
5950
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
5951
        if (live)
5952
            flags |= VIR_DOMAIN_AFFECT_LIVE;
5953 5954 5955 5956
        /* neither option is specified */
        if (!live && !config)
            flags = -1;
    }
5957

5958
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5959
        return false;
5960

J
Jim Meyering 已提交
5961
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5962
        return false;
5963

E
Eric Blake 已提交
5964 5965 5966 5967 5968 5969 5970
    /* The API expects 'unsigned long' KiB, so depending on whether we
     * are 32-bit or 64-bit determines the maximum we can use.  */
    if (sizeof(kibibytes) < sizeof(max))
        max = 1024ull * ULONG_MAX;
    else
        max = ULONG_MAX;
    if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
5971
        vshError(ctl, "%s", _("memory size has to be a number"));
5972
        virDomainFree(dom);
E
Eric Blake 已提交
5973
        return false;
5974
    }
E
Eric Blake 已提交
5975
    kibibytes = VIR_DIV_UP(bytes, 1024);
5976

5977
    if (flags == -1) {
E
Eric Blake 已提交
5978
        if (virDomainSetMaxMemory(dom, kibibytes) != 0) {
5979
            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
E
Eric Blake 已提交
5980
            ret = false;
5981 5982
        }
    } else {
E
Eric Blake 已提交
5983
        if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
5984
            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
E
Eric Blake 已提交
5985
            ret = false;
5986
        }
5987 5988
    }

5989 5990 5991 5992
    virDomainFree(dom);
    return ret;
}

5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008
/*
 * "blkiotune" command
 */
static const vshCmdInfo info_blkiotune[] = {
    {"help", N_("Get or set blkio parameters")},
    {"desc", N_("Get or set the current blkio parameters for a guest" \
                " domain.\n" \
                "    To get the blkio parameters use following command: \n\n" \
                "    virsh # blkiotune <domain>")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_blkiotune[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"weight", VSH_OT_INT, VSH_OFLAG_NONE,
     N_("IO Weight in range [100, 1000]")},
6009 6010
    {"device-weights", VSH_OT_STRING, VSH_OFLAG_NONE,
     N_("per-device IO Weights, in the form of /path/to/device,weight,...")},
H
Hu Tao 已提交
6011 6012 6013
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
6014 6015 6016
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6017
static bool
6018 6019 6020
cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
{
    virDomainPtr dom;
6021
    const char *device_weight = NULL;
6022 6023
    int weight = 0;
    int nparams = 0;
6024
    int rv = 0;
6025
    unsigned int i = 0;
6026
    virTypedParameterPtr params = NULL, temp = NULL;
E
Eric Blake 已提交
6027
    bool ret = false;
H
Hu Tao 已提交
6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044
    unsigned int flags = 0;
    int current = vshCommandOptBool(cmd, "current");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
    }
6045 6046

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6047
        return false;
6048 6049

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
6050
        return false;
6051

6052
    if ((rv = vshCommandOptInt(cmd, "weight", &weight)) < 0) {
6053 6054 6055 6056 6057
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    }

6058
    if (rv > 0) {
6059
        nparams++;
6060
        if (weight <= 0) {
6061 6062 6063 6064 6065
            vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
            goto cleanup;
        }
    }

6066 6067 6068 6069 6070 6071 6072 6073 6074 6075
    rv = vshCommandOptString(cmd, "device-weights", &device_weight);
    if (rv < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse string parameter"));
        goto cleanup;
    }
    if (rv > 0) {
        nparams++;
    }

6076 6077
    if (nparams == 0) {
        /* get the number of blkio parameters */
H
Hu Tao 已提交
6078
        if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
6079 6080 6081 6082 6083 6084 6085
            vshError(ctl, "%s",
                     _("Unable to get number of blkio parameters"));
            goto cleanup;
        }

        if (nparams == 0) {
            /* nothing to output */
E
Eric Blake 已提交
6086
            ret = true;
6087 6088 6089 6090 6091
            goto cleanup;
        }

        /* now go get all the blkio parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));
H
Hu Tao 已提交
6092
        if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
6093 6094 6095 6096 6097
            vshError(ctl, "%s", _("Unable to get blkio parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
6098 6099 6100
            char *str = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
            VIR_FREE(str);
6101 6102 6103 6104 6105 6106 6107
        }
    } else {
        /* set the blkio parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));

        for (i = 0; i < nparams; i++) {
            temp = &params[i];
6108
            temp->type = VIR_TYPED_PARAM_UINT;
6109 6110 6111

            if (weight) {
                temp->value.ui = weight;
6112 6113 6114
                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
                               sizeof(temp->field)))
                    goto cleanup;
6115 6116
                weight = 0;
            } else if (device_weight) {
6117 6118 6119 6120 6121
                temp->value.s = vshStrdup(ctl, device_weight);
                temp->type = VIR_TYPED_PARAM_STRING;
                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
                               sizeof(temp->field)))
                    goto cleanup;
6122
                device_weight = NULL;
6123 6124
            }
        }
6125 6126

        if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0) {
6127
            vshError(ctl, "%s", _("Unable to change blkio parameters"));
6128 6129
            goto cleanup;
        }
6130 6131
    }

6132 6133
    ret = true;

6134
  cleanup:
6135
    virTypedParameterArrayClear(params, nparams);
6136 6137 6138 6139 6140
    VIR_FREE(params);
    virDomainFree(dom);
    return ret;
}

6141 6142 6143 6144
/*
 * "memtune" command
 */
static const vshCmdInfo info_memtune[] = {
6145 6146 6147
    {"help", N_("Get or set memory parameters")},
    {"desc", N_("Get or set the current memory parameters for a guest" \
                " domain.\n" \
6148 6149 6150 6151 6152 6153 6154
                "    To get the memory parameters use following command: \n\n" \
                "    virsh # memtune <domain>")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_memtune[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
6155
    {"hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
E
Eric Blake 已提交
6156
     N_("Max memory, as scaled integer (default KiB)")},
6157
    {"soft-limit", VSH_OT_INT, VSH_OFLAG_NONE,
E
Eric Blake 已提交
6158
     N_("Memory during contention, as scaled integer (default KiB)")},
6159
    {"swap-hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
E
Eric Blake 已提交
6160
     N_("Max memory plus swap, as scaled integer (default KiB)")},
6161
    {"min-guarantee", VSH_OT_INT, VSH_OFLAG_NONE,
E
Eric Blake 已提交
6162
     N_("Min guaranteed memory, as scaled integer (default KiB)")},
6163 6164 6165
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
6166 6167 6168
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192
static int
vshMemtuneGetSize(const vshCmd *cmd, const char *name, long long *value)
{
    int ret;
    unsigned long long tmp;
    const char *str;
    char *end;

    ret = vshCommandOptString(cmd, name, &str);
    if (ret <= 0)
        return ret;
    if (virStrToLong_ll(str, &end, 10, value) < 0)
        return -1;
    if (*value < 0) {
        *value = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
        return 1;
    }
    tmp = *value;
    if (virScaleInteger(&tmp, end, 1024, LLONG_MAX) < 0)
        return -1;
    *value = VIR_DIV_UP(tmp, 1024);
    return 0;
}

E
Eric Blake 已提交
6193
static bool
E
Eric Blake 已提交
6194
cmdMemtune(vshControl *ctl, const vshCmd *cmd)
6195 6196
{
    virDomainPtr dom;
6197 6198
    long long hard_limit = 0, soft_limit = 0, swap_hard_limit = 0;
    long long min_guarantee = 0;
6199 6200
    int nparams = 0;
    unsigned int i = 0;
6201
    virTypedParameterPtr params = NULL, temp = NULL;
E
Eric Blake 已提交
6202
    bool ret = false;
6203 6204 6205 6206 6207 6208 6209 6210 6211 6212
    unsigned int flags = 0;
    int current = vshCommandOptBool(cmd, "current");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
6213
        flags = VIR_DOMAIN_AFFECT_CURRENT;
6214 6215
    } else {
        if (config)
6216
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
6217
        if (live)
6218
            flags |= VIR_DOMAIN_AFFECT_LIVE;
6219
    }
6220 6221

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6222
        return false;
6223 6224

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
6225
        return false;
6226

E
Eric Blake 已提交
6227 6228 6229 6230
    if (vshMemtuneGetSize(cmd, "hard-limit", &hard_limit) < 0 ||
        vshMemtuneGetSize(cmd, "soft-limit", &soft_limit) < 0 ||
        vshMemtuneGetSize(cmd, "swap-hard-limit", &swap_hard_limit) < 0 ||
        vshMemtuneGetSize(cmd, "min-guarantee", &min_guarantee) < 0) {
6231 6232 6233 6234 6235
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    }

6236 6237 6238 6239 6240 6241 6242 6243 6244
    if (hard_limit)
        nparams++;

    if (soft_limit)
        nparams++;

    if (swap_hard_limit)
        nparams++;

6245 6246 6247
    if (min_guarantee)
        nparams++;

6248 6249
    if (nparams == 0) {
        /* get the number of memory parameters */
6250
        if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
6251 6252 6253 6254 6255
            vshError(ctl, "%s",
                     _("Unable to get number of memory parameters"));
            goto cleanup;
        }

6256 6257
        if (nparams == 0) {
            /* nothing to output */
E
Eric Blake 已提交
6258
            ret = true;
6259 6260 6261
            goto cleanup;
        }

6262
        /* now go get all the memory parameters */
E
Eric Blake 已提交
6263
        params = vshCalloc(ctl, nparams, sizeof(*params));
6264
        if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
6265 6266 6267 6268 6269
            vshError(ctl, "%s", _("Unable to get memory parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
6270 6271 6272 6273 6274 6275 6276
            if (params[i].type == VIR_TYPED_PARAM_ULLONG &&
                params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED) {
                vshPrint(ctl, "%-15s: %s\n", params[i].field, _("unlimited"));
            } else {
                char *str = vshGetTypedParamValue(ctl, &params[i]);
                vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
                VIR_FREE(str);
6277 6278 6279
            }
        }

E
Eric Blake 已提交
6280
        ret = true;
6281 6282
    } else {
        /* set the memory parameters */
E
Eric Blake 已提交
6283
        params = vshCalloc(ctl, nparams, sizeof(*params));
6284 6285 6286

        for (i = 0; i < nparams; i++) {
            temp = &params[i];
6287
            temp->type = VIR_TYPED_PARAM_ULLONG;
6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306

            /*
             * Some magic here, this is used to fill the params structure with
             * the valid arguments passed, after filling the particular
             * argument we purposely make them 0, so on the next pass it goes
             * to the next valid argument and so on.
             */
            if (soft_limit) {
                temp->value.ul = soft_limit;
                strncpy(temp->field, VIR_DOMAIN_MEMORY_SOFT_LIMIT,
                        sizeof(temp->field));
                soft_limit = 0;
            } else if (hard_limit) {
                temp->value.ul = hard_limit;
                strncpy(temp->field, VIR_DOMAIN_MEMORY_HARD_LIMIT,
                        sizeof(temp->field));
                hard_limit = 0;
            } else if (swap_hard_limit) {
                temp->value.ul = swap_hard_limit;
6307
                strncpy(temp->field, VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
6308 6309
                        sizeof(temp->field));
                swap_hard_limit = 0;
6310 6311 6312 6313 6314
            } else if (min_guarantee) {
                temp->value.ul = min_guarantee;
                strncpy(temp->field, VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
                        sizeof(temp->field));
                min_guarantee = 0;
6315
            }
6316 6317 6318 6319

            /* If the user has passed -1, we interpret it as unlimited */
            if (temp->value.ul == -1)
                temp->value.ul = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
6320
        }
6321
        if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
6322
            vshError(ctl, "%s", _("Unable to change memory parameters"));
6323
        else
E
Eric Blake 已提交
6324
            ret = true;
6325 6326 6327
    }

  cleanup:
E
Eric Blake 已提交
6328
    VIR_FREE(params);
6329 6330 6331 6332
    virDomainFree(dom);
    return ret;
}

H
Hu Tao 已提交
6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 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 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 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 6446 6447 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 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490
/*
 * "numatune" command
 */
static const vshCmdInfo info_numatune[] = {
    {"help", N_("Get or set numa parameters")},
    {"desc", N_("Get or set the current numa parameters for a guest" \
                " domain.\n" \
                "    To get the numa parameters use following command: \n\n" \
                "    virsh # numatune <domain>")},
    {NULL, NULL}

};

static const vshCmdOptDef opts_numatune[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"mode", VSH_OT_DATA, VSH_OFLAG_NONE,
     N_("NUMA mode, one of strict, preferred and interleave")},
    {"nodeset", VSH_OT_DATA, VSH_OFLAG_NONE,
     N_("NUMA node selections to set")},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
    {NULL, 0, 0, NULL}
};

static bool
cmdNumatune(vshControl * ctl, const vshCmd * cmd)
{
    virDomainPtr dom;
    int nparams = 0;
    unsigned int i = 0;
    virTypedParameterPtr params = NULL, temp = NULL;
    const char *nodeset = NULL;
    bool ret = false;
    unsigned int flags = 0;
    int current = vshCommandOptBool(cmd, "current");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    const char *mode = NULL;

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
    }

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

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

    if (vshCommandOptString(cmd, "nodeset", &nodeset) < 0) {
        vshError(ctl, "%s", _("Unable to parse nodeset."));
        virDomainFree(dom);
        return false;
    }
    if (nodeset)
        nparams++;
    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
        vshError(ctl, "%s", _("Unable to parse mode."));
        virDomainFree(dom);
        return false;
    }
    if (mode)
        nparams++;

    if (nparams == 0) {
        /* get the number of numa parameters */
        if (virDomainGetNumaParameters(dom, NULL, &nparams, flags) != 0) {
            vshError(ctl, "%s",
                     _("Unable to get number of memory parameters"));
            goto cleanup;
        }

        if (nparams == 0) {
            /* nothing to output */
            ret = true;
            goto cleanup;
        }

        /* now go get all the numa parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));
        if (virDomainGetNumaParameters(dom, params, &nparams, flags) != 0) {
            vshError(ctl, "%s", _("Unable to get numa parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
            if (params[i].type == VIR_TYPED_PARAM_INT &&
                STREQ(params[i].field, VIR_DOMAIN_NUMA_MODE)) {
                vshPrint(ctl, "%-15s: %s\n", params[i].field,
                         virDomainNumatuneMemModeTypeToString(params[i].value.i));
            } else {
                char *str = vshGetTypedParamValue(ctl, &params[i]);
                vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
                VIR_FREE(str);
            }
        }

        ret = true;
    } else {
        /* set the numa parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));

        for (i = 0; i < nparams; i++) {
            temp = &params[i];

            /*
             * Some magic here, this is used to fill the params structure with
             * the valid arguments passed, after filling the particular
             * argument we purposely make them 0, so on the next pass it goes
             * to the next valid argument and so on.
             */
            if (mode) {
                /* Accept string or integer, in case server
                 * understands newer integer than what strings we were
                 * compiled with */
                if ((temp->value.i =
                    virDomainNumatuneMemModeTypeFromString(mode)) < 0 &&
                    virStrToLong_i(mode, NULL, 0, &temp->value.i) < 0) {
                    vshError(ctl, _("Invalid mode: %s"), mode);
                    goto cleanup;
                }
                if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_MODE,
                               sizeof(temp->field)))
                    goto cleanup;
                temp->type = VIR_TYPED_PARAM_INT;
                mode = NULL;
            } else if (nodeset) {
                temp->value.s = vshStrdup(ctl, nodeset);
                temp->type = VIR_TYPED_PARAM_STRING;
                if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_NODESET,
                               sizeof(temp->field)))
                    goto cleanup;
                nodeset = NULL;
            }
        }
        if (virDomainSetNumaParameters(dom, params, nparams, flags) != 0)
            vshError(ctl, "%s", _("Unable to change numa parameters"));
        else
            ret = true;
    }

  cleanup:
    virTypedParameterArrayClear(params, nparams);
    VIR_FREE(params);
    virDomainFree(dom);
    return ret;
}

6491 6492 6493
/*
 * "nodeinfo" command
 */
6494
static const vshCmdInfo info_nodeinfo[] = {
6495 6496
    {"help", N_("node information")},
    {"desc", N_("Returns basic information about the node.")},
6497 6498 6499
    {NULL, NULL}
};

E
Eric Blake 已提交
6500
static bool
6501
cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
6502 6503
{
    virNodeInfo info;
6504

6505
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6506
        return false;
6507 6508

    if (virNodeGetInfo(ctl->conn, &info) < 0) {
6509
        vshError(ctl, "%s", _("failed to get node information"));
E
Eric Blake 已提交
6510
        return false;
6511 6512 6513 6514 6515 6516 6517 6518 6519 6520
    }
    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 kB\n", _("Memory size:"), info.memory);

E
Eric Blake 已提交
6521
    return true;
6522 6523
}

6524 6525 6526 6527 6528
/*
 * "nodecpustats" command
 */
static const vshCmdInfo info_nodecpustats[] = {
    {"help", N_("Prints cpu stats of the node.")},
6529
    {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
6530 6531 6532 6533 6534 6535 6536 6537 6538 6539
    {NULL, NULL}
};

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.")},
    {NULL, 0, 0, NULL}
};

static bool
6540
cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
6541 6542 6543 6544
{
    int i, j;
    bool flag_utilization = false;
    bool flag_percent = vshCommandOptBool(cmd, "percent");
6545 6546
    int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
    virNodeCPUStatsPtr params;
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 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589
    int nparams = 0;
    bool ret = false;
    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;

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

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

    if (virNodeGetCPUStats(ctl->conn, cpuNum, NULL, &nparams, 0) != 0) {
        vshError(ctl, "%s",
                 _("Unable to get number of cpu stats"));
        return false;
    }
    if (nparams == 0) {
        /* nothing to output */
        return true;
    }

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

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

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

6590
            if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
6591
                cpu_stats[i].sys = value;
6592
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
6593
                cpu_stats[i].user = value;
6594
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
6595
                cpu_stats[i].idle = value;
6596
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
6597
                cpu_stats[i].iowait = value;
6598
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612
                cpu_stats[i].util = value;
                flag_utilization = true;
            }
        }

        if (flag_utilization || !flag_percent)
            break;

        i++;
        sleep(1);
    } while (i < 2);

    if (!flag_percent) {
        if (!flag_utilization) {
6613
            vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
6614
            vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
6615
            vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
6616 6617 6618 6619 6620 6621 6622
            vshPrint(ctl, "%-15s %20llu\n", _("iowait:"), cpu_stats[0].iowait);
        }
    } else {
        if (flag_utilization) {
            usage = cpu_stats[0].util;

            vshPrint(ctl, "%-15s %5.1lf%%\n", _("usage:"), usage);
6623
            vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635
        } 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;

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

            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("usage:"), usage);
            vshPrint(ctl, "%-15s %5.1lf%%\n",
6636
                     _("user:"), user_time / total_time * 100);
6637
            vshPrint(ctl, "%-15s %5.1lf%%\n",
6638
                     _("system:"), sys_time  / total_time * 100);
6639
            vshPrint(ctl, "%-15s %5.1lf%%\n",
6640
                     _("idle:"), idle_time     / total_time * 100);
6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("iowait:"), iowait_time   / total_time * 100);
        }
    }

    ret = true;

  cleanup:
    VIR_FREE(params);
    return ret;
}

6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671
/*
 * "nodememstats" command
 */
static const vshCmdInfo info_nodememstats[] = {
    {"help", N_("Prints memory stats of the node.")},
    {"desc", N_("Returns memory stats of the node, in kilobytes.")},
    {NULL, NULL}
};

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

static bool
cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd)
{
    int nparams = 0;
    unsigned int i = 0;
6672 6673
    int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
    virNodeMemoryStatsPtr params = NULL;
6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713
    bool ret = false;

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

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

    /* 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"));
        goto cleanup;
    }

    if (nparams == 0) {
        /* nothing to output */
        ret = true;
        goto cleanup;
    }

    /* 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"));
        goto cleanup;
    }

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

    ret = true;

  cleanup:
    VIR_FREE(params);
    return ret;
}

6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742
/*
 * "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}
};

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

static bool
cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
{
    const char *target = NULL;
    unsigned int suspendTarget;
    long long duration;
    unsigned int flags = 0;

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

6743 6744
    if (vshCommandOptString(cmd, "target", &target) < 0) {
        vshError(ctl, _("Invalid target argument"));
6745
        return false;
6746
    }
6747

6748 6749
    if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
        vshError(ctl, _("Invalid duration argument"));
6750
        return false;
6751
    }
6752

6753 6754
    if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
        vshError(ctl, _("Invalid flags argument"));
6755
        return false;
6756
    }
6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782

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

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

    if (virNodeSuspendForDuration(ctl->conn, suspendTarget, duration,
                                  flags) < 0) {
        vshError(ctl, "%s", _("The host was not suspended"));
        return false;
    }
    return true;
}


6783 6784 6785
/*
 * "capabilities" command
 */
6786
static const vshCmdInfo info_capabilities[] = {
6787 6788
    {"help", N_("capabilities")},
    {"desc", N_("Returns capabilities of hypervisor/driver.")},
6789 6790 6791
    {NULL, NULL}
};

E
Eric Blake 已提交
6792
static bool
6793
cmdCapabilities (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
6794 6795 6796
{
    char *caps;

6797
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6798
        return false;
6799 6800

    if ((caps = virConnectGetCapabilities (ctl->conn)) == NULL) {
6801
        vshError(ctl, "%s", _("failed to get capabilities"));
E
Eric Blake 已提交
6802
        return false;
6803 6804
    }
    vshPrint (ctl, "%s\n", caps);
6805
    VIR_FREE(caps);
6806

E
Eric Blake 已提交
6807
    return true;
6808 6809
}

6810 6811 6812
/*
 * "dumpxml" command
 */
6813
static const vshCmdInfo info_dumpxml[] = {
6814 6815
    {"help", N_("domain information in XML")},
    {"desc", N_("Output the domain information as an XML dump to stdout.")},
6816
    {NULL, NULL}
6817 6818
};

6819
static const vshCmdOptDef opts_dumpxml[] = {
6820 6821 6822
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
    {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
6823
    {"update-cpu", VSH_OT_BOOL, 0, N_("update guest CPU according to host CPU")},
6824
    {NULL, 0, 0, NULL}
6825 6826
};

E
Eric Blake 已提交
6827
static bool
6828
cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
6829
{
6830
    virDomainPtr dom;
E
Eric Blake 已提交
6831
    bool ret = true;
6832
    char *dump;
E
Eric Blake 已提交
6833
    unsigned int flags = 0;
6834 6835
    int inactive = vshCommandOptBool(cmd, "inactive");
    int secure = vshCommandOptBool(cmd, "security-info");
6836
    int update = vshCommandOptBool(cmd, "update-cpu");
6837 6838 6839 6840 6841

    if (inactive)
        flags |= VIR_DOMAIN_XML_INACTIVE;
    if (secure)
        flags |= VIR_DOMAIN_XML_SECURE;
6842 6843
    if (update)
        flags |= VIR_DOMAIN_XML_UPDATE_CPU;
6844

6845
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6846
        return false;
6847

J
Jim Meyering 已提交
6848
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
6849
        return false;
6850

6851
    dump = virDomainGetXMLDesc(dom, flags);
6852
    if (dump != NULL) {
6853
        vshPrint(ctl, "%s", dump);
6854
        VIR_FREE(dump);
6855
    } else {
E
Eric Blake 已提交
6856
        ret = false;
6857
    }
6858

6859 6860 6861 6862
    virDomainFree(dom);
    return ret;
}

6863 6864 6865 6866
/*
 * "domxml-from-native" command
 */
static const vshCmdInfo info_domxmlfromnative[] = {
6867 6868
    {"help", N_("Convert native config to domain XML")},
    {"desc", N_("Convert native guest configuration format to domain XML format.")},
6869 6870 6871 6872
    {NULL, NULL}
};

static const vshCmdOptDef opts_domxmlfromnative[] = {
6873 6874
    {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source config data format")},
    {"config", VSH_OT_DATA, VSH_OFLAG_REQ, N_("config data file to import from")},
6875 6876 6877
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6878
static bool
6879 6880
cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
6881
    bool ret = true;
6882 6883
    const char *format = NULL;
    const char *configFile = NULL;
6884 6885
    char *configData;
    char *xmlData;
E
Eric Blake 已提交
6886
    unsigned int flags = 0;
6887

6888
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6889
        return false;
6890

6891 6892
    if (vshCommandOptString(cmd, "format", &format) < 0 ||
        vshCommandOptString(cmd, "config", &configFile) < 0)
E
Eric Blake 已提交
6893
        return false;
6894

6895
    if (virFileReadAll(configFile, 1024*1024, &configData) < 0)
E
Eric Blake 已提交
6896
        return false;
6897 6898 6899

    xmlData = virConnectDomainXMLFromNative(ctl->conn, format, configData, flags);
    if (xmlData != NULL) {
6900
        vshPrint(ctl, "%s", xmlData);
6901
        VIR_FREE(xmlData);
6902
    } else {
E
Eric Blake 已提交
6903
        ret = false;
6904 6905
    }

6906
    VIR_FREE(configData);
6907 6908 6909 6910 6911 6912 6913
    return ret;
}

/*
 * "domxml-to-native" command
 */
static const vshCmdInfo info_domxmltonative[] = {
6914 6915
    {"help", N_("Convert domain XML to native config")},
    {"desc", N_("Convert domain XML config to a native guest configuration format.")},
6916 6917 6918 6919
    {NULL, NULL}
};

static const vshCmdOptDef opts_domxmltonative[] = {
6920 6921
    {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target config data type format")},
    {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, N_("xml data file to export from")},
6922 6923 6924
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6925
static bool
6926 6927
cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
6928
    bool ret = true;
6929 6930
    const char *format = NULL;
    const char *xmlFile = NULL;
6931 6932
    char *configData;
    char *xmlData;
E
Eric Blake 已提交
6933
    unsigned int flags = 0;
6934

6935
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6936
        return false;
6937

6938 6939
    if (vshCommandOptString(cmd, "format", &format) < 0
        || vshCommandOptString(cmd, "xml", &xmlFile) < 0)
E
Eric Blake 已提交
6940
        return false;
6941

6942
    if (virFileReadAll(xmlFile, 1024*1024, &xmlData) < 0)
E
Eric Blake 已提交
6943
        return false;
6944 6945 6946

    configData = virConnectDomainXMLToNative(ctl->conn, format, xmlData, flags);
    if (configData != NULL) {
6947
        vshPrint(ctl, "%s", configData);
6948
        VIR_FREE(configData);
6949
    } else {
E
Eric Blake 已提交
6950
        ret = false;
6951 6952
    }

6953
    VIR_FREE(xmlData);
6954 6955 6956
    return ret;
}

K
Karel Zak 已提交
6957
/*
K
Karel Zak 已提交
6958
 * "domname" command
K
Karel Zak 已提交
6959
 */
6960
static const vshCmdInfo info_domname[] = {
6961
    {"help", N_("convert a domain id or UUID to domain name")},
6962
    {"desc", ""},
6963
    {NULL, NULL}
K
Karel Zak 已提交
6964 6965
};

6966
static const vshCmdOptDef opts_domname[] = {
6967
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or uuid")},
6968
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
6969 6970
};

E
Eric Blake 已提交
6971
static bool
6972
cmdDomname(vshControl *ctl, const vshCmd *cmd)
6973
{
K
Karel Zak 已提交
6974 6975
    virDomainPtr dom;

6976
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6977
        return false;
J
Jim Meyering 已提交
6978
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
6979
                                      VSH_BYID|VSH_BYUUID)))
E
Eric Blake 已提交
6980
        return false;
6981

K
Karel Zak 已提交
6982 6983
    vshPrint(ctl, "%s\n", virDomainGetName(dom));
    virDomainFree(dom);
E
Eric Blake 已提交
6984
    return true;
K
Karel Zak 已提交
6985 6986 6987
}

/*
K
Karel Zak 已提交
6988
 * "domid" command
K
Karel Zak 已提交
6989
 */
6990
static const vshCmdInfo info_domid[] = {
6991
    {"help", N_("convert a domain name or UUID to domain id")},
6992
    {"desc", ""},
6993
    {NULL, NULL}
K
Karel Zak 已提交
6994 6995
};

6996
static const vshCmdOptDef opts_domid[] = {
6997
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
6998
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
6999 7000
};

E
Eric Blake 已提交
7001
static bool
7002
cmdDomid(vshControl *ctl, const vshCmd *cmd)
7003
{
7004
    virDomainPtr dom;
7005
    unsigned int id;
K
Karel Zak 已提交
7006

7007
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7008
        return false;
J
Jim Meyering 已提交
7009
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
7010
                                      VSH_BYNAME|VSH_BYUUID)))
E
Eric Blake 已提交
7011
        return false;
7012

7013 7014
    id = virDomainGetID(dom);
    if (id == ((unsigned int)-1))
7015
        vshPrint(ctl, "%s\n", "-");
7016
    else
7017
        vshPrint(ctl, "%d\n", id);
K
Karel Zak 已提交
7018
    virDomainFree(dom);
E
Eric Blake 已提交
7019
    return true;
K
Karel Zak 已提交
7020
}
7021

K
Karel Zak 已提交
7022 7023 7024
/*
 * "domuuid" command
 */
7025
static const vshCmdInfo info_domuuid[] = {
7026
    {"help", N_("convert a domain name or id to domain UUID")},
7027
    {"desc", ""},
K
Karel Zak 已提交
7028 7029 7030
    {NULL, NULL}
};

7031
static const vshCmdOptDef opts_domuuid[] = {
7032
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or name")},
K
Karel Zak 已提交
7033 7034 7035
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7036
static bool
7037
cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
K
Karel Zak 已提交
7038 7039
{
    virDomainPtr dom;
7040
    char uuid[VIR_UUID_STRING_BUFLEN];
K
Karel Zak 已提交
7041

7042
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7043
        return false;
J
Jim Meyering 已提交
7044
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
7045
                                      VSH_BYNAME|VSH_BYID)))
E
Eric Blake 已提交
7046
        return false;
7047

K
Karel Zak 已提交
7048 7049 7050
    if (virDomainGetUUIDString(dom, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
7051
        vshError(ctl, "%s", _("failed to get domain UUID"));
7052

7053
    virDomainFree(dom);
E
Eric Blake 已提交
7054
    return true;
K
Karel Zak 已提交
7055 7056
}

7057 7058 7059
/*
 * "migrate" command
 */
7060
static const vshCmdInfo info_migrate[] = {
7061 7062
    {"help", N_("migrate domain to another host")},
    {"desc", N_("Migrate domain to another host.  Add --live for live migration.")},
7063 7064 7065
    {NULL, NULL}
};

7066
static const vshCmdOptDef opts_migrate[] = {
7067 7068 7069
    {"live", VSH_OT_BOOL, 0, N_("live migration")},
    {"p2p", VSH_OT_BOOL, 0, N_("peer-2-peer migration")},
    {"direct", VSH_OT_BOOL, 0, N_("direct migration")},
E
Eric Blake 已提交
7070
    {"tunneled", VSH_OT_ALIAS, 0, "tunnelled"},
7071 7072 7073 7074
    {"tunnelled", VSH_OT_BOOL, 0, N_("tunnelled migration")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist VM on destination")},
    {"undefinesource", VSH_OT_BOOL, 0, N_("undefine VM on source")},
    {"suspend", VSH_OT_BOOL, 0, N_("do not restart the domain on the destination host")},
7075 7076
    {"copy-storage-all", VSH_OT_BOOL, 0, N_("migration with non-shared storage with full disk copy")},
    {"copy-storage-inc", VSH_OT_BOOL, 0, N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)")},
7077 7078
    {"change-protection", VSH_OT_BOOL, 0,
     N_("prevent any configuration changes to domain until migration ends)")},
7079
    {"unsafe", VSH_OT_BOOL, 0, N_("force migration even if it may be unsafe")},
W
Wen Congyang 已提交
7080
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of migration")},
7081
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
7082
    {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, N_("connection URI of the destination host as seen from the client(normal migration) or source(p2p migration)")},
7083 7084
    {"migrateuri", VSH_OT_DATA, 0, N_("migration URI, usually can be omitted")},
    {"dname", VSH_OT_DATA, 0, N_("rename to new name during migration (if supported)")},
W
Wen Congyang 已提交
7085
    {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")},
7086
    {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")},
7087 7088 7089
    {NULL, 0, 0, NULL}
};

7090 7091
static void
doMigrate (void *opaque)
7092
{
7093
    char ret = '1';
7094
    virDomainPtr dom = NULL;
7095 7096 7097
    const char *desturi = NULL;
    const char *migrateuri = NULL;
    const char *dname = NULL;
E
Eric Blake 已提交
7098
    unsigned int flags = 0;
7099 7100 7101
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
7102 7103
    const char *xmlfile = NULL;
    char *xml = NULL;
7104
    sigset_t sigmask, oldsigmask;
7105 7106 7107 7108 7109

    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
        goto out_sig;
7110

7111
    if (!vshConnectionUsability (ctl, ctl->conn))
7112
        goto out;
7113

J
Jim Meyering 已提交
7114
    if (!(dom = vshCommandOptDomain (ctl, cmd, NULL)))
7115
        goto out;
7116

7117 7118
    if (vshCommandOptString(cmd, "desturi", &desturi) <= 0 ||
        vshCommandOptString(cmd, "migrateuri", &migrateuri) < 0 ||
7119 7120
        vshCommandOptString(cmd, "dname", &dname) < 0) {
        vshError(ctl, "%s", _("missing argument"));
7121
        goto out;
7122
    }
7123

7124 7125 7126 7127 7128
    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
        vshError(ctl, "%s", _("malformed xml argument"));
        goto out;
    }

7129 7130
    if (vshCommandOptBool (cmd, "live"))
        flags |= VIR_MIGRATE_LIVE;
7131 7132
    if (vshCommandOptBool (cmd, "p2p"))
        flags |= VIR_MIGRATE_PEER2PEER;
C
Chris Lalancette 已提交
7133
    if (vshCommandOptBool (cmd, "tunnelled"))
7134
        flags |= VIR_MIGRATE_TUNNELLED;
C
Chris Lalancette 已提交
7135

C
Chris Lalancette 已提交
7136 7137 7138 7139 7140
    if (vshCommandOptBool (cmd, "persistent"))
        flags |= VIR_MIGRATE_PERSIST_DEST;
    if (vshCommandOptBool (cmd, "undefinesource"))
        flags |= VIR_MIGRATE_UNDEFINE_SOURCE;

7141 7142 7143
    if (vshCommandOptBool (cmd, "suspend"))
        flags |= VIR_MIGRATE_PAUSED;

7144 7145 7146 7147 7148 7149
    if (vshCommandOptBool (cmd, "copy-storage-all"))
        flags |= VIR_MIGRATE_NON_SHARED_DISK;

    if (vshCommandOptBool (cmd, "copy-storage-inc"))
        flags |= VIR_MIGRATE_NON_SHARED_INC;

7150 7151
    if (vshCommandOptBool (cmd, "change-protection"))
        flags |= VIR_MIGRATE_CHANGE_PROTECTION;
7152

7153 7154 7155
    if (vshCommandOptBool(cmd, "unsafe"))
        flags |= VIR_MIGRATE_UNSAFE;

7156
    if (xmlfile &&
7157 7158
        virFileReadAll(xmlfile, 8192, &xml) < 0) {
        vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
7159
        goto out;
7160
    }
7161

7162 7163 7164 7165
    if ((flags & VIR_MIGRATE_PEER2PEER) ||
        vshCommandOptBool (cmd, "direct")) {
        /* For peer2peer migration or direct migration we only expect one URI
         * a libvirt URI, or a hypervisor specific URI. */
7166

7167
        if (migrateuri != NULL) {
J
Jim Fehlig 已提交
7168
            vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration"));
7169
            goto out;
7170
        }
7171

7172
        if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0)
7173
            ret = '0';
7174 7175 7176 7177 7178 7179
    } else {
        /* For traditional live migration, connect to the destination host directly. */
        virConnectPtr dconn = NULL;
        virDomainPtr ddom = NULL;

        dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0);
7180
        if (!dconn) goto out;
7181

7182
        ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0);
7183 7184
        if (ddom) {
            virDomainFree(ddom);
7185
            ret = '0';
7186 7187 7188
        }
        virConnectClose (dconn);
    }
7189

7190 7191 7192
out:
    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
out_sig:
7193
    if (dom) virDomainFree (dom);
7194
    VIR_FREE(xml);
7195 7196 7197
    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
}

W
Wen Congyang 已提交
7198
static void
7199 7200
print_job_progress(const char *label, unsigned long long remaining,
                   unsigned long long total)
W
Wen Congyang 已提交
7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219
{
    int progress;

    if (total == 0)
        /* migration has not been started */
        return;

    if (remaining == 0) {
        /* migration has completed */
        progress = 100;
    } else {
        /* use float to avoid overflow */
        progress = (int)(100.0 - remaining * 100.0 / total);
        if (progress >= 100) {
            /* migration has not completed, do not print [100 %] */
            progress = 99;
        }
    }

7220 7221
    /* see comments in vshError about why we must flush */
    fflush(stdout);
7222
    fprintf(stderr, "\r%s: [%3d %%]", label, progress);
7223
    fflush(stderr);
W
Wen Congyang 已提交
7224 7225
}

7226 7227 7228 7229 7230 7231 7232 7233 7234 7235
static void
vshMigrationTimeout(vshControl *ctl,
                    virDomainPtr dom,
                    void *opaque ATTRIBUTE_UNUSED)
{
    vshDebug(ctl, VSH_ERR_DEBUG, "suspending the domain, "
             "since migration timed out\n");
    virDomainSuspend(dom);
}

E
Eric Blake 已提交
7236
static bool
7237 7238 7239 7240 7241 7242 7243 7244
vshWatchJob(vshControl *ctl,
            virDomainPtr dom,
            bool verbose,
            int pipe_fd,
            int timeout,
            jobWatchTimeoutFunc timeout_func,
            void *opaque,
            const char *label)
7245 7246 7247
{
    struct sigaction sig_action;
    struct sigaction old_sig_action;
7248
    struct pollfd pollfd;
W
Wen Congyang 已提交
7249
    struct timeval start, curr;
7250 7251 7252 7253
    virDomainJobInfo jobinfo;
    int ret = -1;
    char retchar;
    bool functionReturn = false;
7254 7255 7256 7257
    sigset_t sigmask, oldsigmask;

    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
7258 7259 7260 7261 7262 7263 7264

    intCaught = 0;
    sig_action.sa_sigaction = vshCatchInt;
    sig_action.sa_flags = SA_SIGINFO;
    sigemptyset(&sig_action.sa_mask);
    sigaction(SIGINT, &sig_action, &old_sig_action);

7265
    pollfd.fd = pipe_fd;
7266 7267 7268
    pollfd.events = POLLIN;
    pollfd.revents = 0;

W
Wen Congyang 已提交
7269
    GETTIMEOFDAY(&start);
W
Wen Congyang 已提交
7270
    while (1) {
7271
repoll:
W
Wen Congyang 已提交
7272 7273
        ret = poll(&pollfd, 1, 500);
        if (ret > 0) {
7274 7275 7276
            if (pollfd.revents & POLLIN &&
                saferead(pipe_fd, &retchar, sizeof(retchar)) > 0 &&
                retchar == '0') {
W
Wen Congyang 已提交
7277 7278
                    if (verbose) {
                        /* print [100 %] */
7279
                        print_job_progress(label, 0, 1);
W
Wen Congyang 已提交
7280
                    }
7281 7282 7283
                    break;
            }
            goto cleanup;
W
Wen Congyang 已提交
7284 7285 7286 7287 7288 7289 7290 7291 7292 7293
        }

        if (ret < 0) {
            if (errno == EINTR) {
                if (intCaught) {
                    virDomainAbortJob(dom);
                    intCaught = 0;
                } else
                    goto repoll;
            }
7294
            goto cleanup;
W
Wen Congyang 已提交
7295 7296
        }

W
Wen Congyang 已提交
7297 7298 7299 7300
        GETTIMEOFDAY(&curr);
        if ( timeout && ((int)(curr.tv_sec - start.tv_sec)  * 1000 + \
                         (int)(curr.tv_usec - start.tv_usec) / 1000) > timeout * 1000 ) {
            /* suspend the domain when migration timeouts. */
7301 7302 7303
            vshDebug(ctl, VSH_ERR_DEBUG, "%s timeout", label);
            if (timeout_func)
                (timeout_func)(ctl, dom, opaque);
W
Wen Congyang 已提交
7304 7305 7306
            timeout = 0;
        }

W
Wen Congyang 已提交
7307 7308 7309 7310 7311
        if (verbose) {
            pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
            ret = virDomainGetJobInfo(dom, &jobinfo);
            pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
            if (ret == 0)
7312
                print_job_progress(label, jobinfo.dataRemaining,
7313
                                   jobinfo.dataTotal);
7314 7315 7316
        }
    }

7317 7318 7319
    functionReturn = true;

cleanup:
7320
    sigaction(SIGINT, &old_sig_action, NULL);
7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375
    return functionReturn;
}

static bool
cmdMigrate (vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    int p[2] = {-1, -1};
    virThread workerThread;
    bool verbose = false;
    bool functionReturn = false;
    int timeout = 0;
    bool live_flag = false;
    vshCtrlData data;

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

    if (vshCommandOptBool (cmd, "verbose"))
        verbose = true;

    if (vshCommandOptBool (cmd, "live"))
        live_flag = true;
    if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
        if (! live_flag) {
            vshError(ctl, "%s", _("migrate: Unexpected timeout for offline migration"));
            goto cleanup;
        }

        if (timeout < 1) {
            vshError(ctl, "%s", _("migrate: Invalid timeout"));
            goto cleanup;
        }

        /* Ensure that we can multiply by 1000 without overflowing. */
        if (timeout > INT_MAX / 1000) {
            vshError(ctl, "%s", _("migrate: Timeout is too big"));
            goto cleanup;
        }
    }

    if (pipe(p) < 0)
        goto cleanup;

    data.ctl = ctl;
    data.cmd = cmd;
    data.writefd = p[1];

    if (virThreadCreate(&workerThread,
                        true,
                        doMigrate,
                        &data) < 0)
        goto cleanup;
    functionReturn = vshWatchJob(ctl, dom, verbose, p[0], timeout,
                                 vshMigrationTimeout, NULL, _("Migration"));
7376 7377 7378 7379 7380 7381 7382

    virThreadJoin(&workerThread);

cleanup:
    virDomainFree(dom);
    VIR_FORCE_CLOSE(p[0]);
    VIR_FORCE_CLOSE(p[1]);
W
Wen Congyang 已提交
7383
    return functionReturn;
7384 7385
}

7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396
/*
 * "migrate-setmaxdowntime" command
 */
static const vshCmdInfo info_migrate_setmaxdowntime[] = {
    {"help", N_("set maximum tolerable downtime")},
    {"desc", N_("Set maximum tolerable downtime of a domain which is being live-migrated to another host.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_migrate_setmaxdowntime[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
7397
    {"downtime", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum tolerable downtime (in milliseconds) for migration")},
7398 7399 7400
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7401
static bool
7402 7403 7404
cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
7405
    long long downtime = 0;
E
Eric Blake 已提交
7406
    bool ret = false;
7407

7408
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7409
        return false;
7410 7411

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
7412
        return false;
7413

7414 7415
    if (vshCommandOptLongLong(cmd, "downtime", &downtime) < 0 ||
        downtime < 1) {
7416 7417 7418 7419 7420 7421 7422
        vshError(ctl, "%s", _("migrate: Invalid downtime"));
        goto done;
    }

    if (virDomainMigrateSetMaxDowntime(dom, downtime, 0))
        goto done;

E
Eric Blake 已提交
7423
    ret = true;
7424 7425 7426 7427 7428 7429

done:
    virDomainFree(dom);
    return ret;
}

7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473
/*
 * "migrate-setspeed" command
 */
static const vshCmdInfo info_migrate_setspeed[] = {
    {"help", N_("Set the maximum migration bandwidth")},
    {"desc", N_("Set the maximum migration bandwidth (in Mbps) for a domain "
                "which is being migrated to another host.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_migrate_setspeed[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"bandwidth", VSH_OT_INT, VSH_OFLAG_REQ, N_("migration bandwidth limit in Mbps")},
    {NULL, 0, 0, NULL}
};

static bool
cmdMigrateSetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    unsigned long bandwidth = 0;
    bool ret = false;

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

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

    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
        vshError(ctl, "%s", _("migrate: Invalid bandwidth"));
        goto done;
    }

    if (virDomainMigrateSetMaxSpeed(dom, bandwidth, 0) < 0)
        goto done;

    ret = true;

done:
    virDomainFree(dom);
    return ret;
}

7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512
/*
 * "migrate-getspeed" command
 */
static const vshCmdInfo info_migrate_getspeed[] = {
    {"help", N_("Get the maximum migration bandwidth")},
    {"desc", N_("Get the maximum migration bandwidth (in Mbps) for a domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_migrate_getspeed[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {NULL, 0, 0, NULL}
};

static bool
cmdMigrateGetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    unsigned long bandwidth;
    bool ret = false;

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

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

    if (virDomainMigrateGetMaxSpeed(dom, &bandwidth, 0) < 0)
        goto done;

    vshPrint(ctl, "%lu\n", bandwidth);

    ret = true;

done:
    virDomainFree(dom);
    return ret;
}

7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529
typedef enum {
    VSH_CMD_BLOCK_JOB_ABORT = 0,
    VSH_CMD_BLOCK_JOB_INFO = 1,
    VSH_CMD_BLOCK_JOB_SPEED = 2,
    VSH_CMD_BLOCK_JOB_PULL = 3,
} VSH_CMD_BLOCK_JOB_MODE;

static int
blockJobImpl(vshControl *ctl, const vshCmd *cmd,
              virDomainBlockJobInfoPtr info, int mode)
{
    virDomainPtr dom = NULL;
    const char *name, *path;
    unsigned long bandwidth = 0;
    int ret = -1;

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

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
7533
        goto cleanup;
7534 7535

    if (vshCommandOptString(cmd, "path", &path) < 0)
7536
        goto cleanup;
7537

7538 7539
    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
        vshError(ctl, "%s", _("bandwidth must be a number"));
7540
        goto cleanup;
7541
    }
7542

E
Eric Blake 已提交
7543
    if (mode == VSH_CMD_BLOCK_JOB_ABORT) {
7544
        ret = virDomainBlockJobAbort(dom, path, 0);
E
Eric Blake 已提交
7545
    } else if (mode == VSH_CMD_BLOCK_JOB_INFO) {
7546
        ret = virDomainGetBlockJobInfo(dom, path, info, 0);
E
Eric Blake 已提交
7547
    } else if (mode == VSH_CMD_BLOCK_JOB_SPEED) {
7548
        ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
E
Eric Blake 已提交
7549 7550 7551 7552 7553 7554 7555 7556 7557
    } else if (mode == VSH_CMD_BLOCK_JOB_PULL) {
        const char *base = NULL;
        if (vshCommandOptString(cmd, "base", &base) < 0)
            goto cleanup;
        if (base)
            ret = virDomainBlockRebase(dom, path, base, bandwidth, 0);
        else
            ret = virDomainBlockPull(dom, path, bandwidth, 0);
    }
7558

7559
cleanup:
7560 7561
    if (dom)
        virDomainFree(dom);
7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577
    return ret;
}

/*
 * "blockpull" command
 */
static const vshCmdInfo info_block_pull[] = {
    {"help", N_("Populate a disk from its backing image.")},
    {"desc", N_("Populate a disk from its backing image.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_block_pull[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path of disk")},
    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("Bandwidth limit in MB/s")},
E
Eric Blake 已提交
7578 7579
    {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
     N_("path of backing file in chain for a partial pull")},
7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602
    {NULL, 0, 0, NULL}
};

static bool
cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
{
    if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL) != 0)
        return false;
    return true;
}

/*
 * "blockjobinfo" command
 */
static const vshCmdInfo info_block_job[] = {
    {"help", N_("Manage active block operations.")},
    {"desc", N_("Manage active block operations.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_block_job[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path of disk")},
Y
Yuri Chornoivan 已提交
7603
    {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Abort the active job on the specified disk")},
7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646
    {"info", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Get active job information for the specified disk")},
    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("Set the Bandwidth limit in MB/s")},
    {NULL, 0, 0, NULL}
};

static bool
cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
{
    int mode;
    virDomainBlockJobInfo info;
    const char *type;
    int ret;

    if (vshCommandOptBool (cmd, "abort")) {
        mode = VSH_CMD_BLOCK_JOB_ABORT;
    } else if (vshCommandOptBool (cmd, "info")) {
        mode = VSH_CMD_BLOCK_JOB_INFO;
    } else if (vshCommandOptBool (cmd, "bandwidth")) {
        mode = VSH_CMD_BLOCK_JOB_SPEED;
    } else {
        vshError(ctl, "%s",
                 _("One of --abort, --info, or --bandwidth is required"));
        return false;
    }

    ret = blockJobImpl(ctl, cmd, &info, mode);
    if (ret < 0)
        return false;

    if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO)
        return true;

    if (info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL)
        type = "Block Pull";
    else
        type = "Unknown job";

    print_job_progress(type, info.end - info.cur, info.end);
    if (info.bandwidth != 0)
        vshPrint(ctl, "    Bandwidth limit: %lu MB/s\n", info.bandwidth);
    return true;
}

7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657
/*
 * "blockresize" command
 */
static const vshCmdInfo info_block_resize[] = {
    {"help", N_("Resize block device of domain.")},
    {"desc", N_("Resize block device of domain.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_block_resize[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
E
Eric Blake 已提交
7658 7659 7660 7661
    {"path", VSH_OT_DATA, VSH_OFLAG_REQ,
     N_("Fully-qualified path of block device")},
    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
     N_("New size of the block device, as scaled integer (default KiB)")},
7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681
    {NULL, 0, 0, NULL}
};

static bool
cmdBlockResize(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    const char *path = NULL;
    unsigned long long size = 0;
    unsigned int flags = 0;
    int ret = false;

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

    if (vshCommandOptString(cmd, "path", (const char **) &path) < 0) {
        vshError(ctl, "%s", _("Path must not be empty"));
        return false;
    }

E
Eric Blake 已提交
7682
    if (vshCommandOptScaledInt(cmd, "size", &size, 1024, ULLONG_MAX) < 0) {
7683 7684 7685 7686
        vshError(ctl, "%s", _("Unable to parse integer"));
        return false;
    }

E
Eric Blake 已提交
7687 7688 7689 7690 7691
    /* Prefer the older interface of KiB.  */
    if (size % 1024 == 0)
        size /= 1024;
    else
        flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705

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

    if (virDomainBlockResize(dom, path, size, flags) < 0) {
        vshError(ctl, _("Failed to resize block device '%s'"), path);
    } else {
        vshPrint(ctl, _("Block device '%s' is resized"), path);
        ret = true;
    }

    virDomainFree(dom);
    return ret;
}
7706

7707 7708 7709
/*
 * "net-autostart" command
 */
7710
static const vshCmdInfo info_network_autostart[] = {
7711
    {"help", N_("autostart a network")},
7712
    {"desc",
7713
     N_("Configure a network to be automatically started at boot.")},
7714 7715 7716
    {NULL, NULL}
};

7717
static const vshCmdOptDef opts_network_autostart[] = {
7718 7719
    {"network",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
7720 7721 7722
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7723
static bool
7724
cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
7725 7726
{
    virNetworkPtr network;
7727
    const char *name;
7728 7729
    int autostart;

7730
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7731
        return false;
7732

J
Jim Meyering 已提交
7733
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
7734
        return false;
7735 7736 7737 7738

    autostart = !vshCommandOptBool(cmd, "disable");

    if (virNetworkSetAutostart(network, autostart) < 0) {
7739
        if (autostart)
7740
            vshError(ctl, _("failed to mark network %s as autostarted"), name);
7741
        else
7742
            vshError(ctl, _("failed to unmark network %s as autostarted"), name);
7743
        virNetworkFree(network);
E
Eric Blake 已提交
7744
        return false;
7745 7746
    }

7747
    if (autostart)
7748
        vshPrint(ctl, _("Network %s marked as autostarted\n"), name);
7749
    else
7750
        vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name);
7751

L
Laine Stump 已提交
7752
    virNetworkFree(network);
E
Eric Blake 已提交
7753
    return true;
7754
}
K
Karel Zak 已提交
7755

L
Lei Li 已提交
7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767
/*
 * "blkdeviotune" command
 */
static const vshCmdInfo info_blkdeviotune[] = {
    {"help", N_("Set or query a block device I/O tuning parameters.")},
    {"desc", N_("Set or query disk I/O parameters such as block throttling.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_blkdeviotune[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
E
Eric Blake 已提交
7768 7769
    {"total_bytes_sec", VSH_OT_ALIAS, 0, "total-bytes-sec"},
    {"total-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
L
Lei Li 已提交
7770
     N_("total throughput limit in bytes per second")},
E
Eric Blake 已提交
7771 7772
    {"read_bytes_sec", VSH_OT_ALIAS, 0, "read-bytes-sec"},
    {"read-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
L
Lei Li 已提交
7773
     N_("read throughput limit in bytes per second")},
E
Eric Blake 已提交
7774 7775
    {"write_bytes_sec", VSH_OT_ALIAS, 0, "write-bytes-sec"},
    {"write-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
L
Lei Li 已提交
7776
     N_("write throughput limit in bytes per second")},
E
Eric Blake 已提交
7777 7778
    {"total_iops_sec", VSH_OT_ALIAS, 0, "total-iops-sec"},
    {"total-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
L
Lei Li 已提交
7779
     N_("total I/O operations limit per second")},
E
Eric Blake 已提交
7780 7781
    {"read_iops_sec", VSH_OT_ALIAS, 0, "read-iops-sec"},
    {"read-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
L
Lei Li 已提交
7782
     N_("read I/O operations limit per second")},
E
Eric Blake 已提交
7783 7784
    {"write_iops_sec", VSH_OT_ALIAS, 0, "write-iops-sec"},
    {"write-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
L
Lei Li 已提交
7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829
     N_("write I/O operations limit per second")},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
    {NULL, 0, 0, NULL}
};

static bool
cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    const char *name, *disk;
    unsigned long long total_bytes_sec = 0, read_bytes_sec = 0, write_bytes_sec = 0;
    unsigned long long total_iops_sec = 0, read_iops_sec = 0, write_iops_sec = 0;
    int nparams = 0;
    virTypedParameterPtr params = NULL, temp = NULL;
    unsigned int flags = 0, i = 0;
    int rv = 0;
    int current = vshCommandOptBool(cmd, "current");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    bool ret = false;

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
    }

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

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
        goto cleanup;

    if (vshCommandOptString(cmd, "device", &disk) < 0)
        goto cleanup;

E
Eric Blake 已提交
7830 7831
    if ((rv = vshCommandOptULongLong(cmd, "total-bytes-sec",
                                     &total_bytes_sec)) < 0) {
L
Lei Li 已提交
7832 7833 7834 7835 7836 7837 7838
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

E
Eric Blake 已提交
7839 7840
    if ((rv = vshCommandOptULongLong(cmd, "read-bytes-sec",
                                     &read_bytes_sec)) < 0) {
L
Lei Li 已提交
7841 7842 7843 7844 7845 7846 7847
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

E
Eric Blake 已提交
7848 7849
    if ((rv = vshCommandOptULongLong(cmd, "write-bytes-sec",
                                     &write_bytes_sec)) < 0) {
L
Lei Li 已提交
7850 7851 7852 7853 7854 7855 7856
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

E
Eric Blake 已提交
7857 7858
    if ((rv = vshCommandOptULongLong(cmd, "total-iops-sec",
                                     &total_iops_sec)) < 0) {
L
Lei Li 已提交
7859 7860 7861 7862 7863 7864 7865
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

E
Eric Blake 已提交
7866 7867
    if ((rv = vshCommandOptULongLong(cmd, "read-iops-sec",
                                     &read_iops_sec)) < 0) {
L
Lei Li 已提交
7868 7869 7870 7871 7872 7873 7874
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

E
Eric Blake 已提交
7875 7876
    if ((rv = vshCommandOptULongLong(cmd, "write-iops-sec",
                                     &write_iops_sec)) < 0) {
L
Lei Li 已提交
7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

    if (nparams == 0) {

        if ((virDomainGetBlockIoTune(dom, NULL, NULL, &nparams, flags)) != 0) {
            vshError(ctl, "%s",
                     _("Unable to get number of block I/O throttle parameters"));
            goto cleanup;
        }

        if (nparams == 0) {
            ret = true;
            goto cleanup;
        }

        params = vshCalloc(ctl, nparams, sizeof(*params));

        if ((virDomainGetBlockIoTune(dom, disk, params, &nparams, flags)) != 0) {
            vshError(ctl, "%s",
                     _("Unable to get block I/O throttle parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
7906 7907 7908
            char *str = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
            VIR_FREE(str);
L
Lei Li 已提交
7909 7910
        }

7911
        ret = true;
7912
        goto cleanup;
L
Lei Li 已提交
7913 7914 7915 7916 7917
    } else {
        /* Set the block I/O throttle, match by opt since parameters can be 0 */
        params = vshCalloc(ctl, nparams, sizeof(*params));
        i = 0;

E
Eric Blake 已提交
7918
        if ((i < nparams) && (vshCommandOptBool(cmd, "total-bytes-sec"))) {
L
Lei Li 已提交
7919 7920 7921 7922 7923 7924 7925 7926
            temp = &params[i];
            temp->type = VIR_TYPED_PARAM_ULLONG;
            strncpy(temp->field, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
                    sizeof(temp->field));
            temp->value.ul = total_bytes_sec;
            i++;
        }

E
Eric Blake 已提交
7927
        if ((i < nparams) && (vshCommandOptBool(cmd, "read-bytes-sec"))) {
L
Lei Li 已提交
7928 7929 7930 7931 7932 7933 7934 7935
            temp = &params[i];
            temp->type = VIR_TYPED_PARAM_ULLONG;
            strncpy(temp->field, VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
                    sizeof(temp->field));
            temp->value.ul = read_bytes_sec;
            i++;
        }

E
Eric Blake 已提交
7936
        if ((i < nparams) && (vshCommandOptBool(cmd, "write-bytes-sec"))) {
L
Lei Li 已提交
7937 7938 7939 7940 7941 7942 7943 7944
            temp = &params[i];
            temp->type = VIR_TYPED_PARAM_ULLONG;
            strncpy(temp->field, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
                    sizeof(temp->field));
            temp->value.ul = write_bytes_sec;
            i++;
        }

E
Eric Blake 已提交
7945
        if ((i < nparams) && (vshCommandOptBool(cmd, "total-iops-sec"))) {
L
Lei Li 已提交
7946 7947 7948 7949 7950 7951 7952 7953
            temp = &params[i];
            temp->type = VIR_TYPED_PARAM_ULLONG;
            strncpy(temp->field, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
                    sizeof(temp->field));
            temp->value.ul = total_iops_sec;
            i++;
        }

E
Eric Blake 已提交
7954
        if ((i < nparams) && (vshCommandOptBool(cmd, "read-iops-sec"))) {
L
Lei Li 已提交
7955 7956 7957 7958 7959 7960 7961 7962
            temp = &params[i];
            temp->type = VIR_TYPED_PARAM_ULLONG;
            strncpy(temp->field, VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
                    sizeof(temp->field));
            temp->value.ul = read_iops_sec;
            i++;
        }

E
Eric Blake 已提交
7963
        if ((i < nparams) && (vshCommandOptBool(cmd, "write-iops-sec"))) {
L
Lei Li 已提交
7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985
            temp = &params[i];
            temp->type = VIR_TYPED_PARAM_ULLONG;
            strncpy(temp->field, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
                    sizeof(temp->field));
            temp->value.ul = write_iops_sec;
        }

        if ((virDomainSetBlockIoTune(dom, disk, params, nparams, flags)) < 0) {
            vshError(ctl, "%s",
                     _("Unable to change block I/O throttle"));
            goto cleanup;
        }
    }

    ret = true;

cleanup:
    VIR_FREE(params);
    virDomainFree(dom);
    return ret;
}

7986 7987 7988
/*
 * "net-create" command
 */
7989
static const vshCmdInfo info_network_create[] = {
7990 7991
    {"help", N_("create a network from an XML file")},
    {"desc", N_("Create a network.")},
7992 7993 7994
    {NULL, NULL}
};

7995
static const vshCmdOptDef opts_network_create[] = {
7996
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
7997 7998 7999
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8000
static bool
8001
cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
8002 8003
{
    virNetworkPtr network;
8004
    const char *from = NULL;
E
Eric Blake 已提交
8005
    bool ret = true;
8006
    char *buffer;
8007

8008
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8009
        return false;
8010

8011
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
8012
        return false;
8013

8014
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
8015
        return false;
8016 8017

    network = virNetworkCreateXML(ctl->conn, buffer);
8018
    VIR_FREE(buffer);
8019

8020 8021 8022
    if (network != NULL) {
        vshPrint(ctl, _("Network %s created from %s\n"),
                 virNetworkGetName(network), from);
L
Laine Stump 已提交
8023
        virNetworkFree(network);
8024
    } else {
8025
        vshError(ctl, _("Failed to create network from %s"), from);
E
Eric Blake 已提交
8026
        ret = false;
8027 8028 8029 8030 8031 8032 8033 8034
    }
    return ret;
}


/*
 * "net-define" command
 */
8035
static const vshCmdInfo info_network_define[] = {
8036 8037
    {"help", N_("define (but don't start) a network from an XML file")},
    {"desc", N_("Define a network.")},
8038 8039 8040
    {NULL, NULL}
};

8041
static const vshCmdOptDef opts_network_define[] = {
8042
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
8043 8044 8045
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8046
static bool
8047
cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
8048 8049
{
    virNetworkPtr network;
8050
    const char *from = NULL;
E
Eric Blake 已提交
8051
    bool ret = true;
8052
    char *buffer;
8053

8054
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8055
        return false;
8056

8057
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
8058
        return false;
8059

8060
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
8061
        return false;
8062 8063

    network = virNetworkDefineXML(ctl->conn, buffer);
8064
    VIR_FREE(buffer);
8065

8066 8067 8068
    if (network != NULL) {
        vshPrint(ctl, _("Network %s defined from %s\n"),
                 virNetworkGetName(network), from);
L
Laine Stump 已提交
8069
        virNetworkFree(network);
8070
    } else {
8071
        vshError(ctl, _("Failed to define network from %s"), from);
E
Eric Blake 已提交
8072
        ret = false;
8073 8074 8075 8076 8077 8078 8079 8080
    }
    return ret;
}


/*
 * "net-destroy" command
 */
8081
static const vshCmdInfo info_network_destroy[] = {
8082 8083
    {"help", N_("destroy (stop) a network")},
    {"desc", N_("Forcefully stop a given network.")},
8084 8085 8086
    {NULL, NULL}
};

8087
static const vshCmdOptDef opts_network_destroy[] = {
8088
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
8089 8090 8091
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8092
static bool
8093
cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
8094 8095
{
    virNetworkPtr network;
E
Eric Blake 已提交
8096
    bool ret = true;
8097
    const char *name;
8098

8099
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8100
        return false;
8101

J
Jim Meyering 已提交
8102
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
8103
        return false;
8104 8105 8106 8107

    if (virNetworkDestroy(network) == 0) {
        vshPrint(ctl, _("Network %s destroyed\n"), name);
    } else {
8108
        vshError(ctl, _("Failed to destroy network %s"), name);
E
Eric Blake 已提交
8109
        ret = false;
8110 8111
    }

8112
    virNetworkFree(network);
8113 8114 8115 8116 8117 8118 8119
    return ret;
}


/*
 * "net-dumpxml" command
 */
8120
static const vshCmdInfo info_network_dumpxml[] = {
8121 8122
    {"help", N_("network information in XML")},
    {"desc", N_("Output the network information as an XML dump to stdout.")},
8123 8124 8125
    {NULL, NULL}
};

8126
static const vshCmdOptDef opts_network_dumpxml[] = {
8127
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
8128
    {"inactive", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("network information of an inactive domain")},
8129 8130 8131
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8132
static bool
8133
cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
8134 8135
{
    virNetworkPtr network;
E
Eric Blake 已提交
8136
    bool ret = true;
8137
    char *dump;
8138 8139
    unsigned int flags = 0;
    int inactive;
8140

8141
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8142
        return false;
8143

J
Jim Meyering 已提交
8144
    if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
E
Eric Blake 已提交
8145
        return false;
8146

8147 8148 8149 8150 8151 8152
    inactive = vshCommandOptBool (cmd, "inactive");
    if (inactive)
        flags |= VIR_NETWORK_XML_INACTIVE;

    dump = virNetworkGetXMLDesc(network, flags);

8153
    if (dump != NULL) {
8154
        vshPrint(ctl, "%s", dump);
8155
        VIR_FREE(dump);
8156
    } else {
E
Eric Blake 已提交
8157
        ret = false;
8158 8159 8160 8161 8162 8163
    }

    virNetworkFree(network);
    return ret;
}

O
Osier Yang 已提交
8164 8165 8166 8167 8168
/*
 * "net-info" command
 */
static const vshCmdInfo info_network_info[] = {
    {"help", N_("network information")},
E
Eric Blake 已提交
8169
    {"desc", N_("Returns basic information about the network")},
O
Osier Yang 已提交
8170 8171 8172 8173 8174 8175 8176 8177
    {NULL, NULL}
};

static const vshCmdOptDef opts_network_info[] = {
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8178
static bool
O
Osier Yang 已提交
8179 8180 8181 8182 8183 8184 8185 8186 8187 8188
cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
{
    virNetworkPtr network;
    char uuid[VIR_UUID_STRING_BUFLEN];
    int autostart;
    int persistent = -1;
    int active = -1;
    char *bridge = NULL;

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8189
        return false;
O
Osier Yang 已提交
8190 8191 8192

    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
                                           VSH_BYNAME)))
E
Eric Blake 已提交
8193
        return false;
O
Osier Yang 已提交
8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218

    vshPrint(ctl, "%-15s %s\n", _("Name"), virNetworkGetName(network));

    if (virNetworkGetUUIDString(network, uuid) == 0)
        vshPrint(ctl, "%-15s %s\n", _("UUID"), uuid);

    active = virNetworkIsActive(network);
    if (active >= 0)
        vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));

    persistent = virNetworkIsPersistent(network);
    if (persistent < 0)
        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
    else
        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));

    if (virNetworkGetAutostart(network, &autostart) < 0)
        vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
    else
        vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));

    bridge = virNetworkGetBridgeName(network);
    if (bridge)
        vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);

8219
    VIR_FREE(bridge);
O
Osier Yang 已提交
8220
    virNetworkFree(network);
E
Eric Blake 已提交
8221
    return true;
O
Osier Yang 已提交
8222
}
8223

8224 8225 8226 8227
/*
 * "iface-edit" command
 */
static const vshCmdInfo info_interface_edit[] = {
8228 8229
    {"help", N_("edit XML configuration for a physical host interface")},
    {"desc", N_("Edit the XML configuration for a physical host interface.")},
8230 8231 8232 8233
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_edit[] = {
8234
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
8235 8236 8237
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8238
static bool
8239 8240
cmdInterfaceEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
8241
    bool ret = false;
8242 8243 8244 8245 8246
    virInterfacePtr iface = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;
E
Eric Blake 已提交
8247
    unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
8248

8249
    if (!vshConnectionUsability(ctl, ctl->conn))
8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275
        goto cleanup;

    iface = vshCommandOptInterface (ctl, cmd, NULL);
    if (iface == NULL)
        goto cleanup;

    /* Get the XML configuration of the interface. */
    doc = virInterfaceGetXMLDesc (iface, flags);
    if (!doc)
        goto cleanup;

    /* Create and open the temporary file. */
    tmp = editWriteToTempFile (ctl, doc);
    if (!tmp) goto cleanup;

    /* Start the editor. */
    if (editFile (ctl, tmp) == -1) goto cleanup;

    /* Read back the edited file. */
    doc_edited = editReadBackFile (ctl, tmp);
    if (!doc_edited) goto cleanup;

    /* Compare original XML with edited.  Has it changed at all? */
    if (STREQ (doc, doc_edited)) {
        vshPrint (ctl, _("Interface %s XML configuration not changed.\n"),
                  virInterfaceGetName (iface));
E
Eric Blake 已提交
8276
        ret = true;
8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288
        goto cleanup;
    }

    /* Now re-read the interface XML.  Did someone else change it while
     * it was being edited?  This also catches problems such as us
     * losing a connection or the interface going away.
     */
    doc_reread = virInterfaceGetXMLDesc (iface, flags);
    if (!doc_reread)
        goto cleanup;

    if (STRNEQ (doc, doc_reread)) {
8289 8290
        vshError(ctl, "%s",
                 _("ERROR: the XML configuration was changed by another user"));
8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302
        goto cleanup;
    }

    /* Everything checks out, so redefine the interface. */
    virInterfaceFree (iface);
    iface = virInterfaceDefineXML (ctl->conn, doc_edited, 0);
    if (!iface)
        goto cleanup;

    vshPrint (ctl, _("Interface %s XML configuration edited.\n"),
              virInterfaceGetName(iface));

E
Eric Blake 已提交
8303
    ret = true;
8304 8305 8306 8307 8308

cleanup:
    if (iface)
        virInterfaceFree (iface);

8309 8310 8311
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);
8312 8313 8314

    if (tmp) {
        unlink (tmp);
8315
        VIR_FREE(tmp);
8316 8317 8318 8319 8320
    }

    return ret;
}

8321 8322 8323
/*
 * "net-list" command
 */
8324
static const vshCmdInfo info_network_list[] = {
8325 8326
    {"help", N_("list networks")},
    {"desc", N_("Returns list of networks.")},
8327 8328 8329
    {NULL, NULL}
};

8330
static const vshCmdOptDef opts_network_list[] = {
8331 8332
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")},
8333 8334 8335
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8336
static bool
8337
cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
8338 8339 8340 8341 8342
{
    int inactive = vshCommandOptBool(cmd, "inactive");
    int all = vshCommandOptBool(cmd, "all");
    int active = !inactive || all ? 1 : 0;
    int maxactive = 0, maxinactive = 0, i;
8343
    char **activeNames = NULL, **inactiveNames = NULL;
8344 8345
    inactive |= all;

8346
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8347
        return false;
8348 8349

    if (active) {
8350 8351
        maxactive = virConnectNumOfNetworks(ctl->conn);
        if (maxactive < 0) {
8352
            vshError(ctl, "%s", _("Failed to list active networks"));
E
Eric Blake 已提交
8353
            return false;
8354
        }
8355
        if (maxactive) {
8356
            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
8357

8358
            if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
8359
                                                    maxactive)) < 0) {
8360
                vshError(ctl, "%s", _("Failed to list active networks"));
8361
                VIR_FREE(activeNames);
E
Eric Blake 已提交
8362
                return false;
8363
            }
8364

8365
            qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
8366
        }
8367 8368
    }
    if (inactive) {
8369 8370
        maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
        if (maxinactive < 0) {
8371
            vshError(ctl, "%s", _("Failed to list inactive networks"));
8372
            VIR_FREE(activeNames);
E
Eric Blake 已提交
8373
            return false;
8374
        }
8375 8376 8377
        if (maxinactive) {
            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);

8378 8379 8380
            if ((maxinactive =
                     virConnectListDefinedNetworks(ctl->conn, inactiveNames,
                                                   maxinactive)) < 0) {
8381
                vshError(ctl, "%s", _("Failed to list inactive networks"));
8382 8383
                VIR_FREE(activeNames);
                VIR_FREE(inactiveNames);
E
Eric Blake 已提交
8384
                return false;
8385
            }
8386

8387 8388
            qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
        }
8389
    }
8390 8391
    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
                  _("Autostart"));
8392
    vshPrintExtra(ctl, "-----------------------------------------\n");
8393 8394

    for (i = 0; i < maxactive; i++) {
8395 8396
        virNetworkPtr network =
            virNetworkLookupByName(ctl->conn, activeNames[i]);
8397 8398
        const char *autostartStr;
        int autostart = 0;
8399 8400 8401

        /* this kind of work with networks is not atomic operation */
        if (!network) {
8402
            VIR_FREE(activeNames[i]);
8403
            continue;
8404
        }
8405

8406 8407 8408
        if (virNetworkGetAutostart(network, &autostart) < 0)
            autostartStr = _("no autostart");
        else
8409
            autostartStr = autostart ? _("yes") : _("no");
8410 8411 8412 8413 8414

        vshPrint(ctl, "%-20s %-10s %-10s\n",
                 virNetworkGetName(network),
                 _("active"),
                 autostartStr);
8415
        virNetworkFree(network);
8416
        VIR_FREE(activeNames[i]);
8417 8418 8419
    }
    for (i = 0; i < maxinactive; i++) {
        virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
8420 8421
        const char *autostartStr;
        int autostart = 0;
8422 8423 8424

        /* this kind of work with networks is not atomic operation */
        if (!network) {
8425
            VIR_FREE(inactiveNames[i]);
8426
            continue;
8427
        }
8428

8429 8430 8431
        if (virNetworkGetAutostart(network, &autostart) < 0)
            autostartStr = _("no autostart");
        else
8432
            autostartStr = autostart ? _("yes") : _("no");
8433

8434
        vshPrint(ctl, "%-20s %-10s %-10s\n",
8435 8436 8437
                 inactiveNames[i],
                 _("inactive"),
                 autostartStr);
8438 8439

        virNetworkFree(network);
8440
        VIR_FREE(inactiveNames[i]);
8441
    }
8442 8443
    VIR_FREE(activeNames);
    VIR_FREE(inactiveNames);
E
Eric Blake 已提交
8444
    return true;
8445 8446 8447 8448 8449 8450
}


/*
 * "net-name" command
 */
8451
static const vshCmdInfo info_network_name[] = {
8452
    {"help", N_("convert a network UUID to network name")},
8453
    {"desc", ""},
8454 8455 8456
    {NULL, NULL}
};

8457
static const vshCmdOptDef opts_network_name[] = {
8458
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")},
8459 8460 8461
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8462
static bool
8463
cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
8464 8465 8466
{
    virNetworkPtr network;

8467
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8468
        return false;
J
Jim Meyering 已提交
8469
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
8470
                                           VSH_BYUUID)))
E
Eric Blake 已提交
8471
        return false;
8472 8473 8474

    vshPrint(ctl, "%s\n", virNetworkGetName(network));
    virNetworkFree(network);
E
Eric Blake 已提交
8475
    return true;
8476 8477 8478 8479 8480 8481
}


/*
 * "net-start" command
 */
8482
static const vshCmdInfo info_network_start[] = {
8483 8484
    {"help", N_("start a (previously defined) inactive network")},
    {"desc", N_("Start a network.")},
8485 8486 8487
    {NULL, NULL}
};

8488
static const vshCmdOptDef opts_network_start[] = {
8489
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive network")},
8490 8491 8492
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8493
static bool
8494
cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
8495 8496
{
    virNetworkPtr network;
E
Eric Blake 已提交
8497
    bool ret = true;
8498

8499
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8500
        return false;
8501

J
Jim Meyering 已提交
8502
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL, VSH_BYNAME)))
E
Eric Blake 已提交
8503
         return false;
8504 8505 8506

    if (virNetworkCreate(network) == 0) {
        vshPrint(ctl, _("Network %s started\n"),
8507
                 virNetworkGetName(network));
8508
    } else {
8509
        vshError(ctl, _("Failed to start network %s"),
8510
                 virNetworkGetName(network));
E
Eric Blake 已提交
8511
        ret = false;
8512
    }
L
Laine Stump 已提交
8513
    virNetworkFree(network);
8514 8515 8516 8517 8518 8519 8520
    return ret;
}


/*
 * "net-undefine" command
 */
8521
static const vshCmdInfo info_network_undefine[] = {
8522 8523
    {"help", N_("undefine an inactive network")},
    {"desc", N_("Undefine the configuration for an inactive network.")},
8524 8525 8526
    {NULL, NULL}
};

8527
static const vshCmdOptDef opts_network_undefine[] = {
8528
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
8529 8530 8531
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8532
static bool
8533
cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
8534 8535
{
    virNetworkPtr network;
E
Eric Blake 已提交
8536
    bool ret = true;
8537
    const char *name;
8538

8539
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8540
        return false;
8541

J
Jim Meyering 已提交
8542
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
8543
        return false;
8544 8545 8546 8547

    if (virNetworkUndefine(network) == 0) {
        vshPrint(ctl, _("Network %s has been undefined\n"), name);
    } else {
8548
        vshError(ctl, _("Failed to undefine network %s"), name);
E
Eric Blake 已提交
8549
        ret = false;
8550 8551
    }

L
Laine Stump 已提交
8552
    virNetworkFree(network);
8553 8554 8555 8556 8557 8558 8559
    return ret;
}


/*
 * "net-uuid" command
 */
8560
static const vshCmdInfo info_network_uuid[] = {
8561
    {"help", N_("convert a network name to network UUID")},
8562
    {"desc", ""},
8563 8564 8565
    {NULL, NULL}
};

8566
static const vshCmdOptDef opts_network_uuid[] = {
8567
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
8568 8569 8570
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8571
static bool
8572
cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
8573 8574 8575 8576
{
    virNetworkPtr network;
    char uuid[VIR_UUID_STRING_BUFLEN];

8577
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8578
        return false;
8579

J
Jim Meyering 已提交
8580
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
8581
                                           VSH_BYNAME)))
E
Eric Blake 已提交
8582
        return false;
8583 8584 8585 8586

    if (virNetworkGetUUIDString(network, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
8587
        vshError(ctl, "%s", _("failed to get network UUID"));
8588

L
Laine Stump 已提交
8589
    virNetworkFree(network);
E
Eric Blake 已提交
8590
    return true;
8591 8592 8593
}


8594 8595 8596 8597 8598
/**************************************************************************/
/*
 * "iface-list" command
 */
static const vshCmdInfo info_interface_list[] = {
8599 8600
    {"help", N_("list physical host interfaces")},
    {"desc", N_("Returns list of physical host interfaces.")},
8601 8602 8603 8604
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_list[] = {
8605 8606
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive interfaces")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")},
8607 8608
    {NULL, 0, 0, NULL}
};
E
Eric Blake 已提交
8609
static bool
8610 8611 8612 8613 8614 8615 8616 8617 8618
cmdInterfaceList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    int inactive = vshCommandOptBool(cmd, "inactive");
    int all = vshCommandOptBool(cmd, "all");
    int active = !inactive || all ? 1 : 0;
    int maxactive = 0, maxinactive = 0, i;
    char **activeNames = NULL, **inactiveNames = NULL;
    inactive |= all;

8619
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8620
        return false;
8621 8622 8623 8624

    if (active) {
        maxactive = virConnectNumOfInterfaces(ctl->conn);
        if (maxactive < 0) {
8625
            vshError(ctl, "%s", _("Failed to list active interfaces"));
E
Eric Blake 已提交
8626
            return false;
8627 8628 8629 8630 8631 8632
        }
        if (maxactive) {
            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);

            if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames,
                                                    maxactive)) < 0) {
8633
                vshError(ctl, "%s", _("Failed to list active interfaces"));
8634
                VIR_FREE(activeNames);
E
Eric Blake 已提交
8635
                return false;
8636 8637 8638 8639 8640 8641 8642 8643
            }

            qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
        }
    }
    if (inactive) {
        maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn);
        if (maxinactive < 0) {
8644
            vshError(ctl, "%s", _("Failed to list inactive interfaces"));
8645
            VIR_FREE(activeNames);
E
Eric Blake 已提交
8646
            return false;
8647 8648 8649 8650
        }
        if (maxinactive) {
            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);

8651 8652 8653
            if ((maxinactive =
                     virConnectListDefinedInterfaces(ctl->conn, inactiveNames,
                                                     maxinactive)) < 0) {
8654
                vshError(ctl, "%s", _("Failed to list inactive interfaces"));
8655 8656
                VIR_FREE(activeNames);
                VIR_FREE(inactiveNames);
E
Eric Blake 已提交
8657
                return false;
8658 8659 8660 8661 8662
            }

            qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
        }
    }
8663 8664
    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
                  _("MAC Address"));
8665 8666 8667
    vshPrintExtra(ctl, "--------------------------------------------\n");

    for (i = 0; i < maxactive; i++) {
8668 8669
        virInterfacePtr iface =
            virInterfaceLookupByName(ctl->conn, activeNames[i]);
8670 8671 8672

        /* this kind of work with interfaces is not atomic */
        if (!iface) {
8673
            VIR_FREE(activeNames[i]);
8674 8675 8676 8677 8678 8679 8680 8681
            continue;
        }

        vshPrint(ctl, "%-20s %-10s %s\n",
                 virInterfaceGetName(iface),
                 _("active"),
                 virInterfaceGetMACString(iface));
        virInterfaceFree(iface);
8682
        VIR_FREE(activeNames[i]);
8683 8684
    }
    for (i = 0; i < maxinactive; i++) {
8685 8686
        virInterfacePtr iface =
            virInterfaceLookupByName(ctl->conn, inactiveNames[i]);
8687 8688 8689

        /* this kind of work with interfaces is not atomic */
        if (!iface) {
8690
            VIR_FREE(inactiveNames[i]);
8691 8692 8693 8694 8695 8696 8697 8698
            continue;
        }

        vshPrint(ctl, "%-20s %-10s %s\n",
                 virInterfaceGetName(iface),
                 _("inactive"),
                 virInterfaceGetMACString(iface));
        virInterfaceFree(iface);
8699
        VIR_FREE(inactiveNames[i]);
8700
    }
8701 8702
    VIR_FREE(activeNames);
    VIR_FREE(inactiveNames);
E
Eric Blake 已提交
8703
    return true;
8704 8705 8706 8707 8708 8709 8710

}

/*
 * "iface-name" command
 */
static const vshCmdInfo info_interface_name[] = {
8711
    {"help", N_("convert an interface MAC address to interface name")},
8712 8713 8714 8715 8716
    {"desc", ""},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_name[] = {
8717
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface mac")},
8718 8719 8720
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8721
static bool
8722 8723 8724 8725
cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;

8726
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8727
        return false;
8728
    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
8729
                                           VSH_BYMAC)))
E
Eric Blake 已提交
8730
        return false;
8731 8732 8733

    vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
    virInterfaceFree(iface);
E
Eric Blake 已提交
8734
    return true;
8735 8736 8737 8738 8739 8740
}

/*
 * "iface-mac" command
 */
static const vshCmdInfo info_interface_mac[] = {
8741
    {"help", N_("convert an interface name to interface MAC address")},
8742 8743 8744 8745 8746
    {"desc", ""},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_mac[] = {
8747
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
8748 8749 8750
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8751
static bool
8752 8753 8754 8755
cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;

8756
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8757
        return false;
8758
    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
8759
                                           VSH_BYNAME)))
E
Eric Blake 已提交
8760
        return false;
8761 8762 8763

    vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
    virInterfaceFree(iface);
E
Eric Blake 已提交
8764
    return true;
8765 8766 8767 8768 8769 8770
}

/*
 * "iface-dumpxml" command
 */
static const vshCmdInfo info_interface_dumpxml[] = {
8771 8772
    {"help", N_("interface information in XML")},
    {"desc", N_("Output the physical host interface information as an XML dump to stdout.")},
8773 8774 8775 8776
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_dumpxml[] = {
8777 8778
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
8779 8780 8781
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8782
static bool
8783 8784 8785
cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8786
    bool ret = true;
8787
    char *dump;
E
Eric Blake 已提交
8788
    unsigned int flags = 0;
8789 8790 8791 8792
    int inactive = vshCommandOptBool(cmd, "inactive");

    if (inactive)
        flags |= VIR_INTERFACE_XML_INACTIVE;
8793

8794
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8795
        return false;
8796 8797

    if (!(iface = vshCommandOptInterface(ctl, cmd, NULL)))
E
Eric Blake 已提交
8798
        return false;
8799

8800
    dump = virInterfaceGetXMLDesc(iface, flags);
8801
    if (dump != NULL) {
8802
        vshPrint(ctl, "%s", dump);
8803
        VIR_FREE(dump);
8804
    } else {
E
Eric Blake 已提交
8805
        ret = false;
8806 8807 8808 8809 8810 8811 8812 8813 8814 8815
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-define" command
 */
static const vshCmdInfo info_interface_define[] = {
8816 8817
    {"help", N_("define (but don't start) a physical host interface from an XML file")},
    {"desc", N_("Define a physical host interface.")},
8818 8819 8820 8821
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_define[] = {
8822
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML interface description")},
8823 8824 8825
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8826
static bool
8827 8828
cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
{
8829
    virInterfacePtr iface;
8830
    const char *from = NULL;
E
Eric Blake 已提交
8831
    bool ret = true;
8832 8833
    char *buffer;

8834
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8835
        return false;
8836

8837
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
8838
        return false;
8839 8840

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

8843
    iface = virInterfaceDefineXML(ctl->conn, buffer, 0);
8844
    VIR_FREE(buffer);
8845

8846
    if (iface != NULL) {
8847
        vshPrint(ctl, _("Interface %s defined from %s\n"),
8848
                 virInterfaceGetName(iface), from);
L
Laine Stump 已提交
8849
        virInterfaceFree (iface);
8850
    } else {
8851
        vshError(ctl, _("Failed to define interface from %s"), from);
E
Eric Blake 已提交
8852
        ret = false;
8853 8854 8855 8856 8857 8858 8859 8860
    }
    return ret;
}

/*
 * "iface-undefine" command
 */
static const vshCmdInfo info_interface_undefine[] = {
8861 8862
    {"help", N_("undefine a physical host interface (remove it from configuration)")},
    {"desc", N_("undefine an interface.")},
8863 8864 8865 8866
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_undefine[] = {
8867
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
8868 8869 8870
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8871
static bool
8872 8873 8874
cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8875
    bool ret = true;
8876
    const char *name;
8877

8878
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8879
        return false;
8880 8881

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
8882
        return false;
8883 8884 8885 8886

    if (virInterfaceUndefine(iface) == 0) {
        vshPrint(ctl, _("Interface %s undefined\n"), name);
    } else {
8887
        vshError(ctl, _("Failed to undefine interface %s"), name);
E
Eric Blake 已提交
8888
        ret = false;
8889 8890 8891 8892 8893 8894 8895 8896 8897 8898
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-start" command
 */
static const vshCmdInfo info_interface_start[] = {
8899 8900
    {"help", N_("start a physical host interface (enable it / \"if-up\")")},
    {"desc", N_("start a physical host interface.")},
8901 8902 8903 8904
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_start[] = {
8905
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
8906 8907 8908
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8909
static bool
8910 8911 8912
cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8913
    bool ret = true;
8914
    const char *name;
8915

8916
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8917
        return false;
8918 8919

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
8920
        return false;
8921 8922 8923 8924

    if (virInterfaceCreate(iface, 0) == 0) {
        vshPrint(ctl, _("Interface %s started\n"), name);
    } else {
8925
        vshError(ctl, _("Failed to start interface %s"), name);
E
Eric Blake 已提交
8926
        ret = false;
8927 8928 8929 8930 8931 8932 8933 8934 8935 8936
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-destroy" command
 */
static const vshCmdInfo info_interface_destroy[] = {
8937
    {"help", N_("destroy a physical host interface (disable it / \"if-down\")")},
8938
    {"desc", N_("forcefully stop a physical host interface.")},
8939 8940 8941 8942
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_destroy[] = {
8943
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
8944 8945 8946
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8947
static bool
8948 8949 8950
cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8951
    bool ret = true;
8952
    const char *name;
8953

8954
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8955
        return false;
8956 8957

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
8958
        return false;
8959 8960 8961 8962

    if (virInterfaceDestroy(iface, 0) == 0) {
        vshPrint(ctl, _("Interface %s destroyed\n"), name);
    } else {
8963
        vshError(ctl, _("Failed to destroy interface %s"), name);
E
Eric Blake 已提交
8964
        ret = false;
8965 8966 8967 8968 8969 8970
    }

    virInterfaceFree(iface);
    return ret;
}

8971 8972 8973 8974 8975
/*
 * "iface-begin" command
 */
static const vshCmdInfo info_interface_begin[] = {
    {"help", N_("create a snapshot of current interfaces settings, "
Y
Yuri Chornoivan 已提交
8976
                "which can be later committed (iface-commit) or "
8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055
                "restored (iface-rollback)")},
    {"desc", N_("Create a restore point for interfaces settings")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_begin[] = {
    {NULL, 0, 0, NULL}
};

static bool
cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    if (virInterfaceChangeBegin(ctl->conn, 0) < 0) {
        vshError(ctl, "%s", _("Failed to begin network config change transaction"));
        return false;
    }

    vshPrint(ctl, "%s", _("Network config change transaction started\n"));
    return true;
}

/*
 * "iface-commit" command
 */
static const vshCmdInfo info_interface_commit[] = {
    {"help", N_("commit changes made since iface-begin and free restore point")},
    {"desc", N_("commit changes and free restore point")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_commit[] = {
    {NULL, 0, 0, NULL}
};

static bool
cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    if (virInterfaceChangeCommit(ctl->conn, 0) < 0) {
        vshError(ctl, "%s", _("Failed to commit network config change transaction"));
        return false;
    }

    vshPrint(ctl, "%s", _("Network config change transaction committed\n"));
    return true;
}

/*
 * "iface-rollback" command
 */
static const vshCmdInfo info_interface_rollback[] = {
    {"help", N_("rollback to previous saved configuration created via iface-begin")},
    {"desc", N_("rollback to previous restore point")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_rollback[] = {
    {NULL, 0, 0, NULL}
};

static bool
cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

    if (virInterfaceChangeRollback(ctl->conn, 0) < 0) {
        vshError(ctl, "%s", _("Failed to rollback network config change transaction"));
        return false;
    }

    vshPrint(ctl, "%s", _("Network config change transaction rolled back\n"));
    return true;
}
9056

9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250
/*
 * "iface-bridge" command
 */
static const vshCmdInfo info_interface_bridge[] = {
    {"help", N_("create a bridge device and attach an existing network device to it")},
    {"desc", N_("bridge an existing network device")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_bridge[] = {
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("existing interface name")},
    {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new bridge device name")},
    {"no-stp", VSH_OT_BOOL, 0, N_("do not enable STP for this bridge")},
    {"delay", VSH_OT_INT, 0,
     N_("number of seconds to squelch traffic on newly connected ports")},
    {"no-start", VSH_OT_BOOL, 0, N_("don't start the bridge immediately")},
    {NULL, 0, 0, NULL}
};

static bool
cmdInterfaceBridge(vshControl *ctl, const vshCmd *cmd)
{
    bool ret = false;
    virInterfacePtr if_handle = NULL, br_handle = NULL;
    const char *if_name, *br_name;
    char *if_type = NULL, *if2_name = NULL, *delay_str = NULL;
    bool stp = false, nostart = false;
    unsigned int delay = 0;
    char *if_xml = NULL;
    xmlChar *br_xml = NULL;
    int br_xml_size;
    xmlDocPtr xml_doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr top_node, br_node, if_node, cur;

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

    /* Get a handle to the original device */
    if (!(if_handle = vshCommandOptInterfaceBy(ctl, cmd, "interface",
                                               &if_name, VSH_BYNAME))) {
        goto cleanup;
    }

    /* Name for new bridge device */
    if (vshCommandOptString(cmd, "bridge", &br_name) <= 0) {
        vshError(ctl, "%s", _("Missing bridge device name in command"));
        goto cleanup;
    }

    /* make sure "new" device doesn't already exist */
    if ((br_handle = virInterfaceLookupByName(ctl->conn, br_name))) {
        vshError(ctl, _("Network device %s already exists"), br_name);
        goto cleanup;
    }

    /* use "no-stp" because we want "stp" to default true */
    stp = !vshCommandOptBool(cmd, "no-stp");

    if (vshCommandOptUInt(cmd, "delay", &delay) < 0) {
        vshError(ctl, "%s", _("Unable to parse delay parameter"));
        goto cleanup;
    }

    nostart = vshCommandOptBool(cmd, "no-start");

    /* Get the original interface into an xmlDoc */
    if (!(if_xml = virInterfaceGetXMLDesc(if_handle, VIR_INTERFACE_XML_INACTIVE)))
        goto cleanup;
    if (!(xml_doc = virXMLParseStringCtxt(if_xml,
                                          _("(interface definition)"), &ctxt))) {
        vshError(ctl, _("Failed to parse configuration of %s"), if_name);
        goto cleanup;
    }
    top_node = ctxt->node;

    /* Verify that the original device isn't already a bridge. */
    if (!(if_type = virXMLPropString(top_node, "type"))) {
        vshError(ctl, _("Existing device %s has no type"), if_name);
        goto cleanup;
    }

    if (STREQ(if_type, "bridge")) {
        vshError(ctl, _("Existing device %s is already a bridge"), if_name);
        goto cleanup;
    }

    /* verify the name in the XML matches the device name */
    if (!(if2_name = virXMLPropString(top_node, "name")) ||
        STRNEQ(if2_name, if_name)) {
        vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
                 if2_name, if_name);
        goto cleanup;
    }

    /* Create a <bridge> node under <interface>. */
    if (!(br_node = xmlNewChild(top_node, NULL, BAD_CAST "bridge", NULL))) {
        vshError(ctl, "%s", _("Failed to create bridge node in xml document"));
        goto cleanup;
    }

    /* Set stp and delay attributes in <bridge> according to the
     * commandline options.
     */
    if (!xmlSetProp(br_node, BAD_CAST "stp", BAD_CAST (stp ? "on" : "off"))) {
        vshError(ctl, "%s", _("Failed to set stp attribute in xml document"));
        goto cleanup;
    }

    if ((delay || stp) &&
        ((virAsprintf(&delay_str, "%d", delay) < 0) ||
         !xmlSetProp(br_node, BAD_CAST "delay", BAD_CAST delay_str))) {
        vshError(ctl, _("Failed to set bridge delay %d in xml document"), delay);
        goto cleanup;
    }

    /* Change the type of the outer/master interface to "bridge" and the
     * name to the provided bridge name.
     */
    if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST "bridge")) {
        vshError(ctl, "%s", _("Failed to set bridge interface type to 'bridge' in xml document"));
        goto cleanup;
    }

    if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST br_name)) {
        vshError(ctl, _("Failed to set master bridge interface name to '%s' in xml document"),
            br_name);
        goto cleanup;
    }

    /* Create an <interface> node under <bridge> that uses the
     * original interface's type and name.
     */
    if (!(if_node = xmlNewChild(br_node, NULL, BAD_CAST "interface", NULL))) {
        vshError(ctl, "%s", _("Failed to create interface node under bridge node in xml document"));
        goto cleanup;
    }

    /* set the type of the inner/slave interface to the original
     * if_type, and the name to the original if_name.
     */
    if (!xmlSetProp(if_node, BAD_CAST "type", BAD_CAST if_type)) {
        vshError(ctl, _("Failed to set new slave interface type to '%s' in xml document"),
                 if_name);
        goto cleanup;
    }

    if (!xmlSetProp(if_node, BAD_CAST "name", BAD_CAST if_name)) {
        vshError(ctl, _("Failed to set new slave interface name to '%s' in xml document"),
            br_name);
        goto cleanup;
    }

    /* Cycle through all the nodes under the original <interface>,
     * moving all <mac>, <bond> and <vlan> nodes down into the new
     * lower level <interface>.
     */
    cur = top_node->children;
    while (cur) {
        xmlNodePtr old = cur;

        cur = cur->next;
        if ((old->type == XML_ELEMENT_NODE) &&
            (xmlStrEqual(old->name, BAD_CAST "mac") ||  /* ethernet stuff to move down */
             xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
             xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
            xmlUnlinkNode(old);
            if (!xmlAddChild(if_node, old)) {
                vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
                xmlFreeNode(old);
                goto cleanup;
            }
        }
    }

    /* The document should now be fully converted; write it out to a string. */
    xmlDocDumpMemory(xml_doc, &br_xml, &br_xml_size);

    if (!br_xml || br_xml_size <= 0) {
        vshError(ctl, _("Failed to format new xml document for bridge %s"), br_name);
        goto cleanup;
    }


    /* br_xml is the new interface to define. It will automatically undefine the
     * independent original interface.
     */
    if (!(br_handle = virInterfaceDefineXML(ctl->conn, (char *) br_xml, 0))) {
        vshError(ctl, _("Failed to define new bridge interface %s"),
                 br_name);
        goto cleanup;
    }

    vshPrint(ctl, _("Created bridge %s with attached device %s\n"),
9251
             br_name, if_name);
9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358

    /* start it up unless requested not to */
    if (!nostart) {
        if (virInterfaceCreate(br_handle, 0) < 0) {
            vshError(ctl, _("Failed to start bridge interface %s"), br_name);
            goto cleanup;
        }
        vshPrint(ctl, _("Bridge interface %s started\n"), br_name);
    }

    ret = true;
 cleanup:
    if (if_handle)
       virInterfaceFree(if_handle);
    if (br_handle)
       virInterfaceFree(br_handle);
    VIR_FREE(if_xml);
    VIR_FREE(br_xml);
    VIR_FREE(if_type);
    VIR_FREE(if2_name);
    VIR_FREE(delay_str);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml_doc);
    return ret;
}

/*
 * "iface-unbridge" command
 */
static const vshCmdInfo info_interface_unbridge[] = {
    {"help", N_("undefine a bridge device after detaching its slave device")},
    {"desc", N_("unbridge a network device")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_unbridge[] = {
    {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("current bridge device name")},
    {"no-start", VSH_OT_BOOL, 0,
     N_("don't start the un-slaved interface immediately (not recommended)")},
    {NULL, 0, 0, NULL}
};

static bool
cmdInterfaceUnbridge(vshControl *ctl, const vshCmd *cmd)
{
    bool ret = false;
    virInterfacePtr if_handle = NULL, br_handle = NULL;
    const char *br_name;
    char *if_type = NULL, *if_name = NULL;
    bool nostart = false;
    char *br_xml = NULL;
    xmlChar *if_xml = NULL;
    int if_xml_size;
    xmlDocPtr xml_doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr top_node, br_node, if_node, cur;

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

    /* Get a handle to the original device */
    if (!(br_handle = vshCommandOptInterfaceBy(ctl, cmd, "bridge",
                                               &br_name, VSH_BYNAME))) {
        goto cleanup;
    }

    nostart = vshCommandOptBool(cmd, "no-start");

    /* Get the bridge xml into an xmlDoc */
    if (!(br_xml = virInterfaceGetXMLDesc(br_handle, VIR_INTERFACE_XML_INACTIVE)))
        goto cleanup;
    if (!(xml_doc = virXMLParseStringCtxt(br_xml,
                                          _("(bridge interface definition)"),
                                          &ctxt))) {
        vshError(ctl, _("Failed to parse configuration of %s"), br_name);
        goto cleanup;
    }
    top_node = ctxt->node;

    /* Verify that the device really is a bridge. */
    if (!(if_type = virXMLPropString(top_node, "type"))) {
        vshError(ctl, _("Existing device %s has no type"), br_name);
        goto cleanup;
    }

    if (STRNEQ(if_type, "bridge")) {
        vshError(ctl, _("Device %s is not a bridge"), br_name);
        goto cleanup;
    }
    VIR_FREE(if_type);

    /* verify the name in the XML matches the device name */
    if (!(if_name = virXMLPropString(top_node, "name")) ||
        STRNEQ(if_name, br_name)) {
        vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
                 if_name, br_name);
        goto cleanup;
    }
    VIR_FREE(if_name);

    /* Find the <bridge> node under <interface>. */
    if (!(br_node = virXPathNode("./bridge", ctxt))) {
        vshError(ctl, "%s", _("No bridge node in xml document"));
        goto cleanup;
    }

    if ((if_node = virXPathNode("./bridge/interface[2]", ctxt))) {
Y
Yuri Chornoivan 已提交
9359
        vshError(ctl, "%s", _("Multiple interfaces attached to bridge"));
9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 9394 9395 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469
        goto cleanup;
    }

    if (!(if_node = virXPathNode("./bridge/interface", ctxt))) {
        vshError(ctl, "%s", _("No interface attached to bridge"));
        goto cleanup;
    }

    /* Change the type and name of the outer/master interface to
     * the type/name of the attached slave interface.
     */
    if (!(if_name = virXMLPropString(if_node, "name"))) {
        vshError(ctl, _("Device attached to bridge %s has no name"), br_name);
        goto cleanup;
    }

    if (!(if_type = virXMLPropString(if_node, "type"))) {
        vshError(ctl, _("Attached device %s has no type"), if_name);
        goto cleanup;
    }

    if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST if_type)) {
        vshError(ctl, _("Failed to set interface type to '%s' in xml document"),
                 if_type);
        goto cleanup;
    }

    if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST if_name)) {
        vshError(ctl, _("Failed to set interface name to '%s' in xml document"),
                 if_name);
        goto cleanup;
    }

    /* Cycle through all the nodes under the attached <interface>,
     * moving all <mac>, <bond> and <vlan> nodes up into the toplevel
     * <interface>.
     */
    cur = if_node->children;
    while (cur) {
        xmlNodePtr old = cur;

        cur = cur->next;
        if ((old->type == XML_ELEMENT_NODE) &&
            (xmlStrEqual(old->name, BAD_CAST "mac") ||  /* ethernet stuff to move down */
             xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
             xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
            xmlUnlinkNode(old);
            if (!xmlAddChild(top_node, old)) {
                vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
                xmlFreeNode(old);
                goto cleanup;
            }
        }
    }

    /* The document should now be fully converted; write it out to a string. */
    xmlDocDumpMemory(xml_doc, &if_xml, &if_xml_size);

    if (!if_xml || if_xml_size <= 0) {
        vshError(ctl, _("Failed to format new xml document for un-enslaved interface %s"),
                 if_name);
        goto cleanup;
    }

    /* Destroy and Undefine the bridge device, since we otherwise
     * can't safely define the unattached device.
     */
    if (virInterfaceDestroy(br_handle, 0) < 0) {
        vshError(ctl, _("Failed to destroy bridge interface %s"), br_name);
        goto cleanup;
    }
    if (virInterfaceUndefine(br_handle) < 0) {
        vshError(ctl, _("Failed to undefine bridge interface %s"), br_name);
        goto cleanup;
    }

    /* if_xml is the new interface to define.
     */
    if (!(if_handle = virInterfaceDefineXML(ctl->conn, (char *) if_xml, 0))) {
        vshError(ctl, _("Failed to define new interface %s"), if_name);
        goto cleanup;
    }

    vshPrint(ctl, _("Device %s un-attached from bridge %s\n"),
             if_name, br_name);

    /* unless requested otherwise, undefine the bridge device */
    if (!nostart) {
        if (virInterfaceCreate(if_handle, 0) < 0) {
            vshError(ctl, _("Failed to start interface %s"), if_name);
            goto cleanup;
        }
        vshPrint(ctl, _("Interface %s started\n"), if_name);
    }

    ret = true;
 cleanup:
    if (if_handle)
       virInterfaceFree(if_handle);
    if (br_handle)
       virInterfaceFree(br_handle);
    VIR_FREE(if_xml);
    VIR_FREE(br_xml);
    VIR_FREE(if_type);
    VIR_FREE(if_name);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml_doc);
    return ret;
}

9470 9471 9472 9473 9474 9475 9476 9477 9478 9479 9480 9481 9482 9483
/*
 * "nwfilter-define" command
 */
static const vshCmdInfo info_nwfilter_define[] = {
    {"help", N_("define or update a network filter from an XML file")},
    {"desc", N_("Define a new network filter or update an existing one.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_nwfilter_define[] = {
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network filter description")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9484
static bool
9485 9486 9487
cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
9488
    const char *from = NULL;
E
Eric Blake 已提交
9489
    bool ret = true;
9490 9491
    char *buffer;

9492
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9493
        return false;
9494

9495
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
9496
        return false;
9497 9498

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
9499
        return false;
9500 9501 9502 9503 9504 9505 9506 9507 9508 9509

    nwfilter = virNWFilterDefineXML(ctl->conn, buffer);
    VIR_FREE(buffer);

    if (nwfilter != NULL) {
        vshPrint(ctl, _("Network filter %s defined from %s\n"),
                 virNWFilterGetName(nwfilter), from);
        virNWFilterFree(nwfilter);
    } else {
        vshError(ctl, _("Failed to define network filter from %s"), from);
E
Eric Blake 已提交
9510
        ret = false;
9511 9512 9513 9514 9515 9516 9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529
    }
    return ret;
}


/*
 * "nwfilter-undefine" command
 */
static const vshCmdInfo info_nwfilter_undefine[] = {
    {"help", N_("undefine a network filter")},
    {"desc", N_("Undefine a given network filter.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_nwfilter_undefine[] = {
    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9530
static bool
9531 9532 9533
cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
E
Eric Blake 已提交
9534
    bool ret = true;
9535
    const char *name;
9536

9537
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9538
        return false;
9539 9540

    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name)))
E
Eric Blake 已提交
9541
        return false;
9542 9543 9544 9545 9546

    if (virNWFilterUndefine(nwfilter) == 0) {
        vshPrint(ctl, _("Network filter %s undefined\n"), name);
    } else {
        vshError(ctl, _("Failed to undefine network filter %s"), name);
E
Eric Blake 已提交
9547
        ret = false;
9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568
    }

    virNWFilterFree(nwfilter);
    return ret;
}


/*
 * "nwfilter-dumpxml" command
 */
static const vshCmdInfo info_nwfilter_dumpxml[] = {
    {"help", N_("network filter information in XML")},
    {"desc", N_("Output the network filter information as an XML dump to stdout.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_nwfilter_dumpxml[] = {
    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9569
static bool
9570 9571 9572
cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
E
Eric Blake 已提交
9573
    bool ret = true;
9574 9575
    char *dump;

9576
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9577
        return false;
9578 9579

    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL)))
E
Eric Blake 已提交
9580
        return false;
9581 9582 9583

    dump = virNWFilterGetXMLDesc(nwfilter, 0);
    if (dump != NULL) {
9584
        vshPrint(ctl, "%s", dump);
9585 9586
        VIR_FREE(dump);
    } else {
E
Eric Blake 已提交
9587
        ret = false;
9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606
    }

    virNWFilterFree(nwfilter);
    return ret;
}

/*
 * "nwfilter-list" command
 */
static const vshCmdInfo info_nwfilter_list[] = {
    {"help", N_("list network filters")},
    {"desc", N_("Returns list of network filters.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_nwfilter_list[] = {
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9607
static bool
9608 9609 9610 9611 9612 9613
cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    int numfilters, i;
    char **names;
    char uuid[VIR_UUID_STRING_BUFLEN];

9614
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9615
        return false;
9616 9617 9618 9619

    numfilters = virConnectNumOfNWFilters(ctl->conn);
    if (numfilters < 0) {
        vshError(ctl, "%s", _("Failed to list network filters"));
E
Eric Blake 已提交
9620
        return false;
9621 9622 9623 9624 9625 9626 9627 9628
    }

    names = vshMalloc(ctl, sizeof(char *) * numfilters);

    if ((numfilters = virConnectListNWFilters(ctl->conn, names,
                                              numfilters)) < 0) {
        vshError(ctl, "%s", _("Failed to list network filters"));
        VIR_FREE(names);
E
Eric Blake 已提交
9629
        return false;
9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 9649 9650 9651 9652 9653 9654 9655 9656
    }

    qsort(&names[0], numfilters, sizeof(char *), namesorter);

    vshPrintExtra(ctl, "%-36s  %-20s \n", _("UUID"), _("Name"));
    vshPrintExtra(ctl,
       "----------------------------------------------------------------\n");

    for (i = 0; i < numfilters; i++) {
        virNWFilterPtr nwfilter =
            virNWFilterLookupByName(ctl->conn, names[i]);

        /* this kind of work with networks is not atomic operation */
        if (!nwfilter) {
            VIR_FREE(names[i]);
            continue;
        }

        virNWFilterGetUUIDString(nwfilter, uuid);
        vshPrint(ctl, "%-36s  %-20s\n",
                 uuid,
                 virNWFilterGetName(nwfilter));
        virNWFilterFree(nwfilter);
        VIR_FREE(names[i]);
    }

    VIR_FREE(names);
E
Eric Blake 已提交
9657
    return true;
9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 9673 9674
}


/*
 * "nwfilter-edit" command
 */
static const vshCmdInfo info_nwfilter_edit[] = {
    {"help", N_("edit XML configuration for a network filter")},
    {"desc", N_("Edit the XML configuration for a network filter.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_nwfilter_edit[] = {
    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9675
static bool
9676 9677
cmdNWFilterEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
9678
    bool ret = false;
9679 9680 9681 9682 9683 9684
    virNWFilterPtr nwfilter = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;

9685
    if (!vshConnectionUsability(ctl, ctl->conn))
9686 9687 9688 9689 9690 9691 9692 9693 9694 9695 9696 9697 9698 9699 9700 9701 9702 9703 9704 9705 9706 9707 9708 9709 9710 9711
        goto cleanup;

    nwfilter = vshCommandOptNWFilter (ctl, cmd, NULL);
    if (nwfilter == NULL)
        goto cleanup;

    /* Get the XML configuration of the interface. */
    doc = virNWFilterGetXMLDesc (nwfilter, 0);
    if (!doc)
        goto cleanup;

    /* Create and open the temporary file. */
    tmp = editWriteToTempFile (ctl, doc);
    if (!tmp) goto cleanup;

    /* Start the editor. */
    if (editFile (ctl, tmp) == -1) goto cleanup;

    /* Read back the edited file. */
    doc_edited = editReadBackFile (ctl, tmp);
    if (!doc_edited) goto cleanup;

    /* Compare original XML with edited.  Has it changed at all? */
    if (STREQ (doc, doc_edited)) {
        vshPrint (ctl, _("Network filter %s XML configuration not changed.\n"),
                  virNWFilterGetName (nwfilter));
E
Eric Blake 已提交
9712
        ret = true;
9713 9714 9715 9716 9717 9718 9719 9720 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738
        goto cleanup;
    }

    /* Now re-read the network filter XML.  Did someone else change it while
     * it was being edited?  This also catches problems such as us
     * losing a connection or the interface going away.
     */
    doc_reread = virNWFilterGetXMLDesc (nwfilter, 0);
    if (!doc_reread)
        goto cleanup;

    if (STRNEQ (doc, doc_reread)) {
        vshError(ctl, "%s",
                 _("ERROR: the XML configuration was changed by another user"));
        goto cleanup;
    }

    /* Everything checks out, so redefine the interface. */
    virNWFilterFree (nwfilter);
    nwfilter = virNWFilterDefineXML (ctl->conn, doc_edited);
    if (!nwfilter)
        goto cleanup;

    vshPrint (ctl, _("Network filter %s XML configuration edited.\n"),
              virNWFilterGetName(nwfilter));

E
Eric Blake 已提交
9739
    ret = true;
9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757

cleanup:
    if (nwfilter)
        virNWFilterFree (nwfilter);

    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);

    if (tmp) {
        unlink (tmp);
        VIR_FREE(tmp);
    }

    return ret;
}


9758
/**************************************************************************/
9759
/*
9760
 * "pool-autostart" command
9761
 */
9762
static const vshCmdInfo info_pool_autostart[] = {
9763
    {"help", N_("autostart a pool")},
9764
    {"desc",
9765
     N_("Configure a pool to be automatically started at boot.")},
9766
    {NULL, NULL}
9767 9768
};

9769
static const vshCmdOptDef opts_pool_autostart[] = {
9770 9771
    {"pool",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
9772 9773
    {NULL, 0, 0, NULL}
};
9774

E
Eric Blake 已提交
9775
static bool
9776
cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd)
9777
{
9778
    virStoragePoolPtr pool;
9779
    const char *name;
9780
    int autostart;
9781

9782
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9783
        return false;
9784

9785
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
9786
        return false;
9787

9788
    autostart = !vshCommandOptBool(cmd, "disable");
9789

9790 9791
    if (virStoragePoolSetAutostart(pool, autostart) < 0) {
        if (autostart)
9792
            vshError(ctl, _("failed to mark pool %s as autostarted"), name);
9793
        else
9794
            vshError(ctl, _("failed to unmark pool %s as autostarted"), name);
9795
        virStoragePoolFree(pool);
E
Eric Blake 已提交
9796
        return false;
9797 9798
    }

9799
    if (autostart)
9800
        vshPrint(ctl, _("Pool %s marked as autostarted\n"), name);
9801
    else
9802
        vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name);
9803

L
Laine Stump 已提交
9804
    virStoragePoolFree(pool);
E
Eric Blake 已提交
9805
    return true;
9806 9807
}

9808
/*
9809
 * "pool-create" command
9810
 */
9811
static const vshCmdInfo info_pool_create[] = {
9812 9813
    {"help", N_("create a pool from an XML file")},
    {"desc", N_("Create a pool.")},
9814 9815 9816
    {NULL, NULL}
};

9817
static const vshCmdOptDef opts_pool_create[] = {
J
Jim Meyering 已提交
9818
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
9819
     N_("file containing an XML pool description")},
9820 9821 9822
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9823
static bool
9824
cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
9825
{
9826
    virStoragePoolPtr pool;
9827
    const char *from = NULL;
E
Eric Blake 已提交
9828
    bool ret = true;
9829
    char *buffer;
9830

9831
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9832
        return false;
9833

9834
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
9835
        return false;
9836

9837
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
9838
        return false;
9839

9840
    pool = virStoragePoolCreateXML(ctl->conn, buffer, 0);
9841
    VIR_FREE(buffer);
9842 9843 9844 9845

    if (pool != NULL) {
        vshPrint(ctl, _("Pool %s created from %s\n"),
                 virStoragePoolGetName(pool), from);
L
Laine Stump 已提交
9846
        virStoragePoolFree(pool);
9847
    } else {
9848
        vshError(ctl, _("Failed to create pool from %s"), from);
E
Eric Blake 已提交
9849
        ret = false;
9850 9851
    }
    return ret;
9852 9853
}

9854

9855 9856 9857 9858
/*
 * "nodedev-create" command
 */
static const vshCmdInfo info_node_device_create[] = {
9859
    {"help", N_("create a device defined "
9860
                          "by an XML file on the node")},
9861
    {"desc", N_("Create a device on the node.  Note that this "
9862 9863 9864 9865 9866 9867 9868
                          "command creates devices on the physical host "
                          "that can then be assigned to a virtual machine.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_node_device_create[] = {
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
9869
     N_("file containing an XML description of the device")},
9870 9871 9872
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9873
static bool
9874 9875 9876
cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
{
    virNodeDevicePtr dev = NULL;
9877
    const char *from = NULL;
E
Eric Blake 已提交
9878
    bool ret = true;
9879 9880
    char *buffer;

9881
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9882
        return false;
9883

9884
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
9885
        return false;
9886

9887
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
9888
        return false;
9889 9890

    dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
9891
    VIR_FREE(buffer);
9892 9893 9894 9895

    if (dev != NULL) {
        vshPrint(ctl, _("Node device %s created from %s\n"),
                 virNodeDeviceGetName(dev), from);
L
Laine Stump 已提交
9896
        virNodeDeviceFree(dev);
9897
    } else {
9898
        vshError(ctl, _("Failed to create node device from %s"), from);
E
Eric Blake 已提交
9899
        ret = false;
9900 9901 9902 9903 9904 9905 9906 9907 9908 9909
    }

    return ret;
}


/*
 * "nodedev-destroy" command
 */
static const vshCmdInfo info_node_device_destroy[] = {
9910
    {"help", N_("destroy (stop) a device on the node")},
9911
    {"desc", N_("Destroy a device on the node.  Note that this "
9912
                "command destroys devices on the physical host")},
9913 9914 9915 9916 9917
    {NULL, NULL}
};

static const vshCmdOptDef opts_node_device_destroy[] = {
    {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
9918
     N_("name of the device to be destroyed")},
9919 9920 9921
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9922
static bool
9923 9924 9925
cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
{
    virNodeDevicePtr dev = NULL;
E
Eric Blake 已提交
9926
    bool ret = true;
9927
    const char *name = NULL;
9928

9929
    if (!vshConnectionUsability(ctl, ctl->conn)) {
E
Eric Blake 已提交
9930
        return false;
9931 9932
    }

9933
    if (vshCommandOptString(cmd, "name", &name) <= 0)
E
Eric Blake 已提交
9934
        return false;
9935 9936 9937 9938 9939 9940

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

    if (virNodeDeviceDestroy(dev) == 0) {
        vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
    } else {
9941
        vshError(ctl, _("Failed to destroy node device '%s'"), name);
E
Eric Blake 已提交
9942
        ret = false;
9943 9944 9945 9946 9947 9948 9949
    }

    virNodeDeviceFree(dev);
    return ret;
}


9950
/*
9951
 * XML Building helper for pool-define-as and pool-create-as
9952
 */
9953
static const vshCmdOptDef opts_pool_X_as[] = {
9954 9955 9956 9957 9958 9959 9960 9961
    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the pool")},
    {"print-xml", VSH_OT_BOOL, 0, N_("print XML document, but don't define/create")},
    {"type", VSH_OT_DATA, VSH_OFLAG_REQ, N_("type of the pool")},
    {"source-host", VSH_OT_DATA, 0, N_("source-host for underlying storage")},
    {"source-path", VSH_OT_DATA, 0, N_("source path for underlying storage")},
    {"source-dev", VSH_OT_DATA, 0, N_("source device for underlying storage")},
    {"source-name", VSH_OT_DATA, 0, N_("source name for underlying storage")},
    {"target", VSH_OT_DATA, 0, N_("target for underlying storage")},
9962
    {"source-format", VSH_OT_STRING, 0, N_("format for underlying storage")},
9963 9964 9965
    {NULL, 0, 0, NULL}
};

9966
static int buildPoolXML(const vshCmd *cmd, const char **retname, char **xml) {
9967

9968 9969
    const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL,
               *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL;
9970
    virBuffer buf = VIR_BUFFER_INITIALIZER;
9971

9972
    if (vshCommandOptString(cmd, "name", &name) <= 0)
9973
        goto cleanup;
9974
    if (vshCommandOptString(cmd, "type", &type) <= 0)
9975 9976
        goto cleanup;

9977 9978 9979 9980 9981
    if (vshCommandOptString(cmd, "source-host", &srcHost) < 0 ||
        vshCommandOptString(cmd, "source-path", &srcPath) < 0 ||
        vshCommandOptString(cmd, "source-dev", &srcDev) < 0 ||
        vshCommandOptString(cmd, "source-name", &srcName) < 0 ||
        vshCommandOptString(cmd, "source-format", &srcFormat) < 0 ||
9982 9983
        vshCommandOptString(cmd, "target", &target) < 0) {
        vshError(NULL, "%s", _("missing argument"));
9984
        goto cleanup;
9985
    }
9986

9987 9988
    virBufferAsprintf(&buf, "<pool type='%s'>\n", type);
    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
9989
    if (srcHost || srcPath || srcDev || srcFormat || srcName) {
9990
        virBufferAddLit(&buf, "  <source>\n");
9991

9992
        if (srcHost)
9993
            virBufferAsprintf(&buf, "    <host name='%s'/>\n", srcHost);
9994
        if (srcPath)
9995
            virBufferAsprintf(&buf, "    <dir path='%s'/>\n", srcPath);
9996
        if (srcDev)
9997
            virBufferAsprintf(&buf, "    <device path='%s'/>\n", srcDev);
9998
        if (srcFormat)
9999
            virBufferAsprintf(&buf, "    <format type='%s'/>\n", srcFormat);
10000
        if (srcName)
10001
            virBufferAsprintf(&buf, "    <name>%s</name>\n", srcName);
10002 10003

        virBufferAddLit(&buf, "  </source>\n");
10004 10005
    }
    if (target) {
10006
        virBufferAddLit(&buf, "  <target>\n");
10007
        virBufferAsprintf(&buf, "    <path>%s</path>\n", target);
10008
        virBufferAddLit(&buf, "  </target>\n");
10009
    }
10010 10011 10012 10013
    virBufferAddLit(&buf, "</pool>\n");

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
10014
        return false;
10015
    }
10016 10017 10018

    *xml = virBufferContentAndReset(&buf);
    *retname = name;
E
Eric Blake 已提交
10019
    return true;
10020 10021

cleanup:
10022
    virBufferFreeAndReset(&buf);
E
Eric Blake 已提交
10023
    return false;
10024 10025 10026 10027 10028 10029
}

/*
 * "pool-create-as" command
 */
static const vshCmdInfo info_pool_create_as[] = {
10030 10031
    {"help", N_("create a pool from a set of args")},
    {"desc", N_("Create a pool.")},
10032 10033 10034
    {NULL, NULL}
};

E
Eric Blake 已提交
10035
static bool
10036 10037 10038
cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool;
10039 10040
    const char *name;
    char *xml;
10041
    int printXML = vshCommandOptBool(cmd, "print-xml");
10042

10043
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10044
        return false;
10045 10046

    if (!buildPoolXML(cmd, &name, &xml))
E
Eric Blake 已提交
10047
        return false;
10048

10049
    if (printXML) {
10050
        vshPrint(ctl, "%s", xml);
10051
        VIR_FREE(xml);
10052
    } else {
10053
        pool = virStoragePoolCreateXML(ctl->conn, xml, 0);
10054
        VIR_FREE(xml);
10055

10056 10057 10058 10059
        if (pool != NULL) {
            vshPrint(ctl, _("Pool %s created\n"), name);
            virStoragePoolFree(pool);
        } else {
10060
            vshError(ctl, _("Failed to create pool %s"), name);
E
Eric Blake 已提交
10061
            return false;
10062 10063
        }
    }
E
Eric Blake 已提交
10064
    return true;
10065 10066
}

10067

10068
/*
10069
 * "pool-define" command
10070
 */
10071
static const vshCmdInfo info_pool_define[] = {
10072 10073
    {"help", N_("define (but don't start) a pool from an XML file")},
    {"desc", N_("Define a pool.")},
10074 10075 10076
    {NULL, NULL}
};

10077
static const vshCmdOptDef opts_pool_define[] = {
10078
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML pool description")},
10079 10080 10081
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10082
static bool
10083
cmdPoolDefine(vshControl *ctl, const vshCmd *cmd)
10084
{
10085
    virStoragePoolPtr pool;
10086
    const char *from = NULL;
E
Eric Blake 已提交
10087
    bool ret = true;
10088
    char *buffer;
10089

10090
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10091
        return false;
10092

10093
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
10094
        return false;
10095

10096
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
10097
        return false;
10098

10099
    pool = virStoragePoolDefineXML(ctl->conn, buffer, 0);
10100
    VIR_FREE(buffer);
10101 10102 10103 10104

    if (pool != NULL) {
        vshPrint(ctl, _("Pool %s defined from %s\n"),
                 virStoragePoolGetName(pool), from);
L
Laine Stump 已提交
10105
        virStoragePoolFree(pool);
10106
    } else {
10107
        vshError(ctl, _("Failed to define pool from %s"), from);
E
Eric Blake 已提交
10108
        ret = false;
10109 10110
    }
    return ret;
10111 10112
}

10113

10114 10115 10116
/*
 * "pool-define-as" command
 */
10117
static const vshCmdInfo info_pool_define_as[] = {
10118 10119
    {"help", N_("define a pool from a set of args")},
    {"desc", N_("Define a pool.")},
10120 10121 10122
    {NULL, NULL}
};

E
Eric Blake 已提交
10123
static bool
10124
cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd)
10125 10126
{
    virStoragePoolPtr pool;
10127 10128
    const char *name;
    char *xml;
10129
    int printXML = vshCommandOptBool(cmd, "print-xml");
10130

10131
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10132
        return false;
10133

10134
    if (!buildPoolXML(cmd, &name, &xml))
E
Eric Blake 已提交
10135
        return false;
10136

10137
    if (printXML) {
10138
        vshPrint(ctl, "%s", xml);
10139
        VIR_FREE(xml);
10140
    } else {
10141
        pool = virStoragePoolDefineXML(ctl->conn, xml, 0);
10142
        VIR_FREE(xml);
10143

10144 10145 10146 10147
        if (pool != NULL) {
            vshPrint(ctl, _("Pool %s defined\n"), name);
            virStoragePoolFree(pool);
        } else {
10148
            vshError(ctl, _("Failed to define pool %s"), name);
E
Eric Blake 已提交
10149
            return false;
10150 10151
        }
    }
E
Eric Blake 已提交
10152
    return true;
10153 10154 10155
}


10156
/*
10157
 * "pool-build" command
10158
 */
10159
static const vshCmdInfo info_pool_build[] = {
10160 10161
    {"help", N_("build a pool")},
    {"desc", N_("Build a given pool.")},
10162 10163 10164
    {NULL, NULL}
};

10165
static const vshCmdOptDef opts_pool_build[] = {
10166
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10167 10168
    {"no-overwrite", VSH_OT_BOOL, 0, N_("do not overwrite an existing pool of this type")},
    {"overwrite", VSH_OT_BOOL, 0, N_("overwrite any existing data")},
10169 10170 10171
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10172
static bool
10173
cmdPoolBuild(vshControl *ctl, const vshCmd *cmd)
10174
{
10175
    virStoragePoolPtr pool;
E
Eric Blake 已提交
10176
    bool ret = true;
10177
    const char *name;
10178
    unsigned int flags = 0;
10179

10180
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10181
        return false;
10182

10183
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
10184
        return false;
10185

10186 10187 10188 10189 10190 10191 10192 10193 10194
    if (vshCommandOptBool (cmd, "no-overwrite")) {
        flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;
    }

    if (vshCommandOptBool (cmd, "overwrite")) {
        flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
    }

    if (virStoragePoolBuild(pool, flags) == 0) {
10195
        vshPrint(ctl, _("Pool %s built\n"), name);
10196
    } else {
10197
        vshError(ctl, _("Failed to build pool %s"), name);
E
Eric Blake 已提交
10198
        ret = false;
10199 10200
    }

10201 10202
    virStoragePoolFree(pool);

10203 10204 10205
    return ret;
}

10206
/*
10207
 * "pool-destroy" command
10208
 */
10209
static const vshCmdInfo info_pool_destroy[] = {
10210 10211 10212
    {"help", N_("destroy (stop) a pool")},
    {"desc",
     N_("Forcefully stop a given pool. Raw data in the pool is untouched")},
10213 10214 10215
    {NULL, NULL}
};

10216
static const vshCmdOptDef opts_pool_destroy[] = {
10217
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10218 10219 10220
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10221
static bool
10222
cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd)
10223
{
10224
    virStoragePoolPtr pool;
E
Eric Blake 已提交
10225
    bool ret = true;
10226
    const char *name;
10227

10228
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10229
        return false;
10230

10231
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
10232
        return false;
10233

10234 10235 10236
    if (virStoragePoolDestroy(pool) == 0) {
        vshPrint(ctl, _("Pool %s destroyed\n"), name);
    } else {
10237
        vshError(ctl, _("Failed to destroy pool %s"), name);
E
Eric Blake 已提交
10238
        ret = false;
10239 10240
    }

10241
    virStoragePoolFree(pool);
10242 10243 10244
    return ret;
}

10245

10246
/*
10247 10248
 * "pool-delete" command
 */
10249
static const vshCmdInfo info_pool_delete[] = {
10250 10251
    {"help", N_("delete a pool")},
    {"desc", N_("Delete a given pool.")},
10252 10253 10254
    {NULL, NULL}
};

10255
static const vshCmdOptDef opts_pool_delete[] = {
10256
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10257 10258 10259
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10260
static bool
10261
cmdPoolDelete(vshControl *ctl, const vshCmd *cmd)
10262 10263
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
10264
    bool ret = true;
10265
    const char *name;
10266

10267
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10268
        return false;
10269 10270

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
10271
        return false;
10272 10273

    if (virStoragePoolDelete(pool, 0) == 0) {
D
Daniel Veillard 已提交
10274
        vshPrint(ctl, _("Pool %s deleted\n"), name);
10275
    } else {
10276
        vshError(ctl, _("Failed to delete pool %s"), name);
E
Eric Blake 已提交
10277
        ret = false;
10278 10279
    }

L
Laine Stump 已提交
10280
    virStoragePoolFree(pool);
10281 10282 10283 10284 10285 10286 10287
    return ret;
}


/*
 * "pool-refresh" command
 */
10288
static const vshCmdInfo info_pool_refresh[] = {
10289 10290
    {"help", N_("refresh a pool")},
    {"desc", N_("Refresh a given pool.")},
10291 10292 10293
    {NULL, NULL}
};

10294
static const vshCmdOptDef opts_pool_refresh[] = {
10295
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10296 10297 10298
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10299
static bool
10300
cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd)
10301 10302
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
10303
    bool ret = true;
10304
    const char *name;
10305

10306
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10307
        return false;
10308 10309

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
10310
        return false;
10311 10312 10313 10314

    if (virStoragePoolRefresh(pool, 0) == 0) {
        vshPrint(ctl, _("Pool %s refreshed\n"), name);
    } else {
10315
        vshError(ctl, _("Failed to refresh pool %s"), name);
E
Eric Blake 已提交
10316
        ret = false;
10317 10318 10319 10320 10321 10322 10323 10324 10325 10326
    }
    virStoragePoolFree(pool);

    return ret;
}


/*
 * "pool-dumpxml" command
 */
10327
static const vshCmdInfo info_pool_dumpxml[] = {
10328 10329
    {"help", N_("pool information in XML")},
    {"desc", N_("Output the pool information as an XML dump to stdout.")},
10330 10331 10332
    {NULL, NULL}
};

10333
static const vshCmdOptDef opts_pool_dumpxml[] = {
10334
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10335 10336 10337
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10338
static bool
10339
cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
10340 10341
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
10342
    bool ret = true;
10343 10344
    char *dump;

10345
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10346
        return false;
10347 10348

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
10349
        return false;
10350 10351 10352

    dump = virStoragePoolGetXMLDesc(pool, 0);
    if (dump != NULL) {
10353
        vshPrint(ctl, "%s", dump);
10354
        VIR_FREE(dump);
10355
    } else {
E
Eric Blake 已提交
10356
        ret = false;
10357 10358 10359 10360 10361 10362 10363 10364 10365 10366
    }

    virStoragePoolFree(pool);
    return ret;
}


/*
 * "pool-list" command
 */
10367
static const vshCmdInfo info_pool_list[] = {
10368 10369
    {"help", N_("list pools")},
    {"desc", N_("Returns list of pools.")},
10370 10371 10372
    {NULL, NULL}
};

10373
static const vshCmdOptDef opts_pool_list[] = {
10374 10375
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")},
10376
    {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")},
10377 10378 10379
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10380
static bool
10381
cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
10382
{
10383 10384
    virStoragePoolInfo info;
    char **poolNames = NULL;
W
Wen Congyang 已提交
10385 10386
    int i, ret;
    bool functionReturn;
10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402
    int numActivePools = 0, numInactivePools = 0, numAllPools = 0;
    size_t stringLength = 0, nameStrLength = 0;
    size_t autostartStrLength = 0, persistStrLength = 0;
    size_t stateStrLength = 0, capStrLength = 0;
    size_t allocStrLength = 0, availStrLength = 0;
    struct poolInfoText {
        char *state;
        char *autostart;
        char *persistent;
        char *capacity;
        char *allocation;
        char *available;
    };
    struct poolInfoText *poolInfoTexts = NULL;

    /* Determine the options passed by the user */
10403
    int all = vshCommandOptBool(cmd, "all");
10404 10405
    int details = vshCommandOptBool(cmd, "details");
    int inactive = vshCommandOptBool(cmd, "inactive");
10406 10407 10408
    int active = !inactive || all ? 1 : 0;
    inactive |= all;

10409
    /* Check the connection to libvirtd daemon is still working */
10410
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10411
        return false;
10412

10413
    /* Retrieve the number of active storage pools */
10414
    if (active) {
10415 10416
        numActivePools = virConnectNumOfStoragePools(ctl->conn);
        if (numActivePools < 0) {
10417
            vshError(ctl, "%s", _("Failed to list active pools"));
E
Eric Blake 已提交
10418
            return false;
10419 10420
        }
    }
10421 10422

    /* Retrieve the number of inactive storage pools */
10423
    if (inactive) {
10424 10425
        numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn);
        if (numInactivePools < 0) {
10426
            vshError(ctl, "%s", _("Failed to list inactive pools"));
E
Eric Blake 已提交
10427
            return false;
10428
        }
10429
    }
10430

10431 10432
    /* Determine the total number of pools to list */
    numAllPools = numActivePools + numInactivePools;
10433

10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445
    /* Allocate memory for arrays of storage pool names and info */
    poolNames = vshCalloc(ctl, numAllPools, sizeof(*poolNames));
    poolInfoTexts =
        vshCalloc(ctl, numAllPools, sizeof(*poolInfoTexts));

    /* Retrieve a list of active storage pool names */
    if (active) {
        if ((virConnectListStoragePools(ctl->conn,
                                        poolNames, numActivePools)) < 0) {
            vshError(ctl, "%s", _("Failed to list active pools"));
            VIR_FREE(poolInfoTexts);
            VIR_FREE(poolNames);
E
Eric Blake 已提交
10446
            return false;
10447 10448 10449
        }
    }

10450 10451 10452 10453 10454 10455 10456 10457
    /* Add the inactive storage pools to the end of the name list */
    if (inactive) {
        if ((virConnectListDefinedStoragePools(ctl->conn,
                                               &poolNames[numActivePools],
                                               numInactivePools)) < 0) {
            vshError(ctl, "%s", _("Failed to list inactive pools"));
            VIR_FREE(poolInfoTexts);
            VIR_FREE(poolNames);
E
Eric Blake 已提交
10458
            return false;
10459 10460
        }
    }
10461

10462 10463 10464 10465 10466 10467 10468 10469 10470 10471
    /* Sort the storage pool names */
    qsort(poolNames, numAllPools, sizeof(*poolNames), namesorter);

    /* Collect the storage pool information for display */
    for (i = 0; i < numAllPools; i++) {
        int autostart = 0, persistent = 0;

        /* Retrieve a pool object, looking it up by name */
        virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn,
                                                            poolNames[i]);
10472
        if (!pool) {
10473
            VIR_FREE(poolNames[i]);
10474 10475 10476
            continue;
        }

10477
        /* Retrieve the autostart status of the pool */
10478
        if (virStoragePoolGetAutostart(pool, &autostart) < 0)
10479
            poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart"));
10480
        else
10481 10482 10483 10484 10485 10486
            poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ?
                                                    _("yes") : _("no"));

        /* Retrieve the persistence status of the pool */
        if (details) {
            persistent = virStoragePoolIsPersistent(pool);
10487 10488
            vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n",
                     persistent);
10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 10499
            if (persistent < 0)
                poolInfoTexts[i].persistent = vshStrdup(ctl, _("unknown"));
            else
                poolInfoTexts[i].persistent = vshStrdup(ctl, persistent ?
                                                         _("yes") : _("no"));

            /* Keep the length of persistent string if longest so far */
            stringLength = strlen(poolInfoTexts[i].persistent);
            if (stringLength > persistStrLength)
                persistStrLength = stringLength;
        }
10500

10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612
        /* Collect further extended information about the pool */
        if (virStoragePoolGetInfo(pool, &info) != 0) {
            /* Something went wrong retrieving pool info, cope with it */
            vshError(ctl, "%s", _("Could not retrieve pool information"));
            poolInfoTexts[i].state = vshStrdup(ctl, _("unknown"));
            if (details) {
                poolInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
                poolInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
                poolInfoTexts[i].available = vshStrdup(ctl, _("unknown"));
            }
        } else {
            /* Decide which state string to display */
            if (details) {
                /* --details option was specified, we're using detailed state
                 * strings */
                switch (info.state) {
                case VIR_STORAGE_POOL_INACTIVE:
                    poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
                    break;
                case VIR_STORAGE_POOL_BUILDING:
                    poolInfoTexts[i].state = vshStrdup(ctl, _("building"));
                    break;
                case VIR_STORAGE_POOL_RUNNING:
                    poolInfoTexts[i].state = vshStrdup(ctl, _("running"));
                    break;
                case VIR_STORAGE_POOL_DEGRADED:
                    poolInfoTexts[i].state = vshStrdup(ctl, _("degraded"));
                    break;
                case VIR_STORAGE_POOL_INACCESSIBLE:
                    poolInfoTexts[i].state = vshStrdup(ctl, _("inaccessible"));
                    break;
                }

                /* Create the pool size related strings */
                if (info.state == VIR_STORAGE_POOL_RUNNING ||
                    info.state == VIR_STORAGE_POOL_DEGRADED) {
                    double val;
                    const char *unit;

                    /* Create the capacity output string */
                    val = prettyCapacity(info.capacity, &unit);
                    ret = virAsprintf(&poolInfoTexts[i].capacity,
                                      "%.2lf %s", val, unit);
                    if (ret < 0) {
                        /* An error occurred creating the string, return */
                        goto asprintf_failure;
                    }

                    /* Create the allocation output string */
                    val = prettyCapacity(info.allocation, &unit);
                    ret = virAsprintf(&poolInfoTexts[i].allocation,
                                      "%.2lf %s", val, unit);
                    if (ret < 0) {
                        /* An error occurred creating the string, return */
                        goto asprintf_failure;
                    }

                    /* Create the available space output string */
                    val = prettyCapacity(info.available, &unit);
                    ret = virAsprintf(&poolInfoTexts[i].available,
                                      "%.2lf %s", val, unit);
                    if (ret < 0) {
                        /* An error occurred creating the string, return */
                        goto asprintf_failure;
                    }
                } else {
                    /* Capacity related information isn't available */
                    poolInfoTexts[i].capacity = vshStrdup(ctl, _("-"));
                    poolInfoTexts[i].allocation = vshStrdup(ctl, _("-"));
                    poolInfoTexts[i].available = vshStrdup(ctl, _("-"));
                }

                /* Keep the length of capacity string if longest so far */
                stringLength = strlen(poolInfoTexts[i].capacity);
                if (stringLength > capStrLength)
                    capStrLength = stringLength;

                /* Keep the length of allocation string if longest so far */
                stringLength = strlen(poolInfoTexts[i].allocation);
                if (stringLength > allocStrLength)
                    allocStrLength = stringLength;

                /* Keep the length of available string if longest so far */
                stringLength = strlen(poolInfoTexts[i].available);
                if (stringLength > availStrLength)
                    availStrLength = stringLength;
            } else {
                /* --details option was not specified, only active/inactive
                * state strings are used */
                if (info.state == VIR_STORAGE_POOL_INACTIVE)
                    poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
                else
                    poolInfoTexts[i].state = vshStrdup(ctl, _("active"));
            }
        }

        /* Keep the length of name string if longest so far */
        stringLength = strlen(poolNames[i]);
        if (stringLength > nameStrLength)
            nameStrLength = stringLength;

        /* Keep the length of state string if longest so far */
        stringLength = strlen(poolInfoTexts[i].state);
        if (stringLength > stateStrLength)
            stateStrLength = stringLength;

        /* Keep the length of autostart string if longest so far */
        stringLength = strlen(poolInfoTexts[i].autostart);
        if (stringLength > autostartStrLength)
            autostartStrLength = stringLength;

        /* Free the pool object */
10613 10614 10615
        virStoragePoolFree(pool);
    }

10616 10617 10618 10619 10620 10621 10622 10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633
    /* If the --details option wasn't selected, we output the pool
     * info using the fixed string format from previous versions to
     * maintain backward compatibility.
     */

    /* Output basic info then return if --details option not selected */
    if (!details) {
        /* Output old style header */
        vshPrintExtra(ctl, "%-20s %-10s %-10s\n", _("Name"), _("State"),
                      _("Autostart"));
        vshPrintExtra(ctl, "-----------------------------------------\n");

        /* Output old style pool info */
        for (i = 0; i < numAllPools; i++) {
            vshPrint(ctl, "%-20s %-10s %-10s\n",
                 poolNames[i],
                 poolInfoTexts[i].state,
                 poolInfoTexts[i].autostart);
10634 10635
        }

10636
        /* Cleanup and return */
E
Eric Blake 已提交
10637
        functionReturn = true;
10638 10639
        goto cleanup;
    }
10640

10641 10642 10643 10644 10645 10646 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 10657 10658 10659 10660 10661 10662 10663 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678
    /* We only get here if the --details option was selected. */

    /* Use the length of name header string if it's longest */
    stringLength = strlen(_("Name"));
    if (stringLength > nameStrLength)
        nameStrLength = stringLength;

    /* Use the length of state header string if it's longest */
    stringLength = strlen(_("State"));
    if (stringLength > stateStrLength)
        stateStrLength = stringLength;

    /* Use the length of autostart header string if it's longest */
    stringLength = strlen(_("Autostart"));
    if (stringLength > autostartStrLength)
        autostartStrLength = stringLength;

    /* Use the length of persistent header string if it's longest */
    stringLength = strlen(_("Persistent"));
    if (stringLength > persistStrLength)
        persistStrLength = stringLength;

    /* Use the length of capacity header string if it's longest */
    stringLength = strlen(_("Capacity"));
    if (stringLength > capStrLength)
        capStrLength = stringLength;

    /* Use the length of allocation header string if it's longest */
    stringLength = strlen(_("Allocation"));
    if (stringLength > allocStrLength)
        allocStrLength = stringLength;

    /* Use the length of available header string if it's longest */
    stringLength = strlen(_("Available"));
    if (stringLength > availStrLength)
        availStrLength = stringLength;

    /* Display the string lengths for debugging. */
10679
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest name string = %lu chars\n",
10680
             (unsigned long) nameStrLength);
10681
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest state string = %lu chars\n",
10682
             (unsigned long) stateStrLength);
10683
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest autostart string = %lu chars\n",
10684
             (unsigned long) autostartStrLength);
10685
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest persistent string = %lu chars\n",
10686
             (unsigned long) persistStrLength);
10687
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest capacity string = %lu chars\n",
10688
             (unsigned long) capStrLength);
10689
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest allocation string = %lu chars\n",
10690
             (unsigned long) allocStrLength);
10691
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest available string = %lu chars\n",
10692 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 10712 10713 10714 10715 10716 10717 10718 10719 10720 10721 10722 10723 10724 10725 10726 10727 10728 10729 10730 10731 10732 10733 10734
             (unsigned long) availStrLength);

    /* Create the output template.  Each column is sized according to
     * the longest string.
     */
    char *outputStr;
    ret = virAsprintf(&outputStr,
              "%%-%lus  %%-%lus  %%-%lus  %%-%lus  %%%lus  %%%lus  %%%lus\n",
              (unsigned long) nameStrLength,
              (unsigned long) stateStrLength,
              (unsigned long) autostartStrLength,
              (unsigned long) persistStrLength,
              (unsigned long) capStrLength,
              (unsigned long) allocStrLength,
              (unsigned long) availStrLength);
    if (ret < 0) {
        /* An error occurred creating the string, return */
        goto asprintf_failure;
    }

    /* Display the header */
    vshPrint(ctl, outputStr, _("Name"), _("State"), _("Autostart"),
             _("Persistent"), _("Capacity"), _("Allocation"), _("Available"));
    for (i = nameStrLength + stateStrLength + autostartStrLength
                           + persistStrLength + capStrLength
                           + allocStrLength + availStrLength
                           + 12; i > 0; i--)
        vshPrintExtra(ctl, "-");
    vshPrintExtra(ctl, "\n");

    /* Display the pool info rows */
    for (i = 0; i < numAllPools; i++) {
        vshPrint(ctl, outputStr,
                 poolNames[i],
                 poolInfoTexts[i].state,
                 poolInfoTexts[i].autostart,
                 poolInfoTexts[i].persistent,
                 poolInfoTexts[i].capacity,
                 poolInfoTexts[i].allocation,
                 poolInfoTexts[i].available);
    }

    /* Cleanup and return */
E
Eric Blake 已提交
10735
    functionReturn = true;
10736 10737 10738 10739 10740 10741 10742 10743 10744 10745 10746 10747 10748 10749
    goto cleanup;

asprintf_failure:

    /* Display an appropriate error message then cleanup and return */
    switch (errno) {
    case ENOMEM:
        /* Couldn't allocate memory */
        vshError(ctl, "%s", _("Out of memory"));
        break;
    default:
        /* Some other error */
        vshError(ctl, _("virAsprintf failed (errno %d)"), errno);
    }
E
Eric Blake 已提交
10750
    functionReturn = false;
10751

10752 10753 10754 10755 10756 10757 10758 10759 10760 10761 10762 10763
cleanup:

    /* Safely free the memory allocated in this function */
    for (i = 0; i < numAllPools; i++) {
        /* Cleanup the memory for one pool info structure */
        VIR_FREE(poolInfoTexts[i].state);
        VIR_FREE(poolInfoTexts[i].autostart);
        VIR_FREE(poolInfoTexts[i].persistent);
        VIR_FREE(poolInfoTexts[i].capacity);
        VIR_FREE(poolInfoTexts[i].allocation);
        VIR_FREE(poolInfoTexts[i].available);
        VIR_FREE(poolNames[i]);
10764
    }
10765 10766 10767 10768 10769 10770 10771

    /* Cleanup the memory for the initial arrays*/
    VIR_FREE(poolInfoTexts);
    VIR_FREE(poolNames);

    /* Return the desired value */
    return functionReturn;
10772 10773
}

10774 10775 10776 10777
/*
 * "find-storage-pool-sources-as" command
 */
static const vshCmdInfo info_find_storage_pool_sources_as[] = {
10778 10779
    {"help", N_("find potential storage pool sources")},
    {"desc", N_("Returns XML <sources> document.")},
10780 10781 10782 10783 10784
    {NULL, NULL}
};

static const vshCmdOptDef opts_find_storage_pool_sources_as[] = {
    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
10785 10786 10787
     N_("type of storage pool sources to find")},
    {"host", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional host to query")},
    {"port", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional port to query")},
10788
    {"initiator", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional initiator IQN to use for query")},
10789 10790 10791
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10792
static bool
10793 10794
cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
10795
    const char *type = NULL, *host = NULL;
10796 10797
    char *srcSpec = NULL;
    char *srcList;
10798
    const char *initiator = NULL;
10799

10800 10801
    if (vshCommandOptString(cmd, "type", &type) <= 0 ||
        vshCommandOptString(cmd, "host", &host) < 0 ||
10802 10803
        vshCommandOptString(cmd, "initiator", &initiator) < 0) {
        vshError(ctl,"%s", _("missing argument"));
E
Eric Blake 已提交
10804
        return false;
10805
    }
10806

10807
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10808
        return false;
10809 10810

    if (host) {
10811
        const char *port = NULL;
10812
        virBuffer buf = VIR_BUFFER_INITIALIZER;
10813 10814 10815 10816

        if (vshCommandOptString(cmd, "port", &port) < 0) {
            vshError(ctl, "%s", _("missing argument"));
            virBufferFreeAndReset(&buf);
E
Eric Blake 已提交
10817
            return false;
10818
        }
10819
        virBufferAddLit(&buf, "<source>\n");
10820
        virBufferAsprintf(&buf, "  <host name='%s'", host);
10821
        if (port)
10822
            virBufferAsprintf(&buf, " port='%s'", port);
10823
        virBufferAddLit(&buf, "/>\n");
10824 10825
        if (initiator) {
            virBufferAddLit(&buf, "  <initiator>\n");
10826
            virBufferAsprintf(&buf, "    <iqn name='%s'/>\n", initiator);
10827 10828
            virBufferAddLit(&buf, "  </initiator>\n");
        }
10829 10830 10831
        virBufferAddLit(&buf, "</source>\n");
        if (virBufferError(&buf)) {
            vshError(ctl, "%s", _("Out of memory"));
E
Eric Blake 已提交
10832
            return false;
10833
        }
10834
        srcSpec = virBufferContentAndReset(&buf);
10835 10836 10837
    }

    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
10838
    VIR_FREE(srcSpec);
10839
    if (srcList == NULL) {
10840
        vshError(ctl, _("Failed to find any %s pool sources"), type);
E
Eric Blake 已提交
10841
        return false;
10842 10843
    }
    vshPrint(ctl, "%s", srcList);
10844
    VIR_FREE(srcList);
10845

E
Eric Blake 已提交
10846
    return true;
10847 10848 10849 10850 10851 10852 10853
}


/*
 * "find-storage-pool-sources" command
 */
static const vshCmdInfo info_find_storage_pool_sources[] = {
10854 10855
    {"help", N_("discover potential storage pool sources")},
    {"desc", N_("Returns XML <sources> document.")},
10856 10857 10858 10859 10860
    {NULL, NULL}
};

static const vshCmdOptDef opts_find_storage_pool_sources[] = {
    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
10861
     N_("type of storage pool sources to discover")},
10862
    {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE,
10863
     N_("optional file of source xml to query for pools")},
10864 10865 10866
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10867
static bool
10868 10869
cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
10870
    const char *type = NULL, *srcSpecFile = NULL;
10871
    char *srcSpec = NULL, *srcList;
10872

10873
    if (vshCommandOptString(cmd, "type", &type) <= 0)
E
Eric Blake 已提交
10874
        return false;
10875

10876 10877
    if (vshCommandOptString(cmd, "srcSpec", &srcSpecFile) < 0) {
        vshError(ctl, "%s", _("missing option"));
E
Eric Blake 已提交
10878
        return false;
10879
    }
10880

10881
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10882
        return false;
10883 10884

    if (srcSpecFile && virFileReadAll(srcSpecFile, VIRSH_MAX_XML_FILE, &srcSpec) < 0)
E
Eric Blake 已提交
10885
        return false;
10886 10887

    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
10888
    VIR_FREE(srcSpec);
10889
    if (srcList == NULL) {
10890
        vshError(ctl, _("Failed to find any %s pool sources"), type);
E
Eric Blake 已提交
10891
        return false;
10892 10893
    }
    vshPrint(ctl, "%s", srcList);
10894
    VIR_FREE(srcList);
10895

E
Eric Blake 已提交
10896
    return true;
10897 10898 10899
}


10900 10901 10902
/*
 * "pool-info" command
 */
10903
static const vshCmdInfo info_pool_info[] = {
10904 10905
    {"help", N_("storage pool information")},
    {"desc", N_("Returns basic information about the storage pool.")},
10906 10907 10908
    {NULL, NULL}
};

10909
static const vshCmdOptDef opts_pool_info[] = {
10910
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10911 10912 10913
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10914
static bool
10915
cmdPoolInfo(vshControl *ctl, const vshCmd *cmd)
10916 10917 10918
{
    virStoragePoolInfo info;
    virStoragePoolPtr pool;
10919 10920
    int autostart = 0;
    int persistent = 0;
E
Eric Blake 已提交
10921
    bool ret = true;
10922 10923
    char uuid[VIR_UUID_STRING_BUFLEN];

10924
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10925
        return false;
10926 10927

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
10928
        return false;
10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954

    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStoragePoolGetName(pool));

    if (virStoragePoolGetUUIDString(pool, &uuid[0])==0)
        vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);

    if (virStoragePoolGetInfo(pool, &info) == 0) {
        double val;
        const char *unit;
        switch (info.state) {
        case VIR_STORAGE_POOL_INACTIVE:
            vshPrint(ctl, "%-15s %s\n", _("State:"),
                     _("inactive"));
            break;
        case VIR_STORAGE_POOL_BUILDING:
            vshPrint(ctl, "%-15s %s\n", _("State:"),
                     _("building"));
            break;
        case VIR_STORAGE_POOL_RUNNING:
            vshPrint(ctl, "%-15s %s\n", _("State:"),
                     _("running"));
            break;
        case VIR_STORAGE_POOL_DEGRADED:
            vshPrint(ctl, "%-15s %s\n", _("State:"),
                     _("degraded"));
            break;
10955 10956 10957 10958
        case VIR_STORAGE_POOL_INACCESSIBLE:
            vshPrint(ctl, "%-15s %s\n", _("State:"),
                     _("inaccessible"));
            break;
10959 10960
        }

10961 10962
        /* Check and display whether the pool is persistent or not */
        persistent = virStoragePoolIsPersistent(pool);
10963 10964
        vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n",
                 persistent);
10965 10966 10967 10968 10969 10970 10971
        if (persistent < 0)
            vshPrint(ctl, "%-15s %s\n", _("Persistent:"),  _("unknown"));
        else
            vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));

        /* Check and display whether the pool is autostarted or not */
        virStoragePoolGetAutostart(pool, &autostart);
10972 10973
        vshDebug(ctl, VSH_ERR_DEBUG, "Pool autostart flag value: %d\n",
                 autostart);
10974 10975 10976 10977 10978
        if (autostart < 0)
            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
        else
            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));

10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990
        if (info.state == VIR_STORAGE_POOL_RUNNING ||
            info.state == VIR_STORAGE_POOL_DEGRADED) {
            val = prettyCapacity(info.capacity, &unit);
            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);

            val = prettyCapacity(info.allocation, &unit);
            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);

            val = prettyCapacity(info.available, &unit);
            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit);
        }
    } else {
E
Eric Blake 已提交
10991
        ret = false;
10992 10993 10994 10995 10996 10997 10998 10999 11000 11001
    }

    virStoragePoolFree(pool);
    return ret;
}


/*
 * "pool-name" command
 */
11002
static const vshCmdInfo info_pool_name[] = {
11003
    {"help", N_("convert a pool UUID to pool name")},
11004
    {"desc", ""},
11005 11006 11007
    {NULL, NULL}
};

11008
static const vshCmdOptDef opts_pool_name[] = {
11009
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool uuid")},
11010 11011 11012
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11013
static bool
11014
cmdPoolName(vshControl *ctl, const vshCmd *cmd)
11015 11016 11017
{
    virStoragePoolPtr pool;

11018
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11019
        return false;
11020
    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
11021
                                           VSH_BYUUID)))
E
Eric Blake 已提交
11022
        return false;
11023 11024 11025

    vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
    virStoragePoolFree(pool);
E
Eric Blake 已提交
11026
    return true;
11027 11028 11029 11030 11031 11032
}


/*
 * "pool-start" command
 */
11033
static const vshCmdInfo info_pool_start[] = {
11034 11035
    {"help", N_("start a (previously defined) inactive pool")},
    {"desc", N_("Start a pool.")},
11036 11037 11038
    {NULL, NULL}
};

11039
static const vshCmdOptDef opts_pool_start[] = {
11040
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive pool")},
11041 11042 11043
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11044
static bool
11045
cmdPoolStart(vshControl *ctl, const vshCmd *cmd)
11046 11047
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
11048
    bool ret = true;
11049

11050
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11051
        return false;
11052

11053
    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME)))
E
Eric Blake 已提交
11054
         return false;
11055 11056 11057 11058 11059

    if (virStoragePoolCreate(pool, 0) == 0) {
        vshPrint(ctl, _("Pool %s started\n"),
                 virStoragePoolGetName(pool));
    } else {
11060
        vshError(ctl, _("Failed to start pool %s"), virStoragePoolGetName(pool));
E
Eric Blake 已提交
11061
        ret = false;
11062
    }
L
Laine Stump 已提交
11063 11064

    virStoragePoolFree(pool);
11065 11066 11067 11068
    return ret;
}


11069 11070 11071
/*
 * "vol-create-as" command
 */
11072
static const vshCmdInfo info_vol_create_as[] = {
11073 11074
    {"help", N_("create a volume from a set of args")},
    {"desc", N_("Create a vol.")},
11075 11076 11077
    {NULL, NULL}
};

11078
static const vshCmdOptDef opts_vol_create_as[] = {
11079 11080
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
E
Eric Blake 已提交
11081 11082 11083 11084 11085 11086 11087 11088 11089 11090
    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
     N_("size of the vol, as scaled integer (default bytes)")},
    {"allocation", VSH_OT_STRING, 0,
     N_("initial allocation size, as scaled integer (default bytes)")},
    {"format", VSH_OT_STRING, 0,
     N_("file format type raw,bochs,qcow,qcow2,vmdk")},
    {"backing-vol", VSH_OT_STRING, 0,
     N_("the backing volume if taking a snapshot")},
    {"backing-vol-format", VSH_OT_STRING, 0,
     N_("format of backing volume if taking a snapshot")},
11091 11092 11093
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11094 11095
static int
vshVolSize(const char *data, unsigned long long *val)
11096 11097 11098 11099
{
    char *end;
    if (virStrToLong_ull(data, &end, 10, val) < 0)
        return -1;
E
Eric Blake 已提交
11100
    return virScaleInteger(val, end, 1, ULLONG_MAX);
11101 11102
}

E
Eric Blake 已提交
11103
static bool
11104
cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
11105 11106 11107
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
11108
    char *xml;
11109 11110
    const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL;
    const char *snapshotStrVol = NULL, *snapshotStrFormat = NULL;
11111
    unsigned long long capacity, allocation = 0;
11112
    virBuffer buf = VIR_BUFFER_INITIALIZER;
11113

11114
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11115
        return false;
11116 11117 11118

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
                                     VSH_BYNAME)))
E
Eric Blake 已提交
11119
        return false;
11120

11121
    if (vshCommandOptString(cmd, "name", &name) <= 0)
11122 11123
        goto cleanup;

11124
    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
11125
        goto cleanup;
E
Eric Blake 已提交
11126
    if (vshVolSize(capacityStr, &capacity) < 0)
11127
        vshError(ctl, _("Malformed size %s"), capacityStr);
11128

11129
    if ((vshCommandOptString(cmd, "allocation", &allocationStr) > 0) &&
E
Eric Blake 已提交
11130
        (vshVolSize(allocationStr, &allocation) < 0))
11131
        vshError(ctl, _("Malformed size %s"), allocationStr);
11132

11133 11134 11135 11136 11137 11138 11139
    if (vshCommandOptString(cmd, "format", &format) < 0 ||
        vshCommandOptString(cmd, "backing-vol", &snapshotStrVol) < 0 ||
        vshCommandOptString(cmd, "backing-vol-format",
                            &snapshotStrFormat) < 0) {
        vshError(ctl, "%s", _("missing argument"));
    }

11140

11141
    virBufferAddLit(&buf, "<volume>\n");
11142 11143
    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
    virBufferAsprintf(&buf, "  <capacity>%llu</capacity>\n", capacity);
11144
    if (allocationStr)
11145
        virBufferAsprintf(&buf, "  <allocation>%llu</allocation>\n", allocation);
11146 11147

    if (format) {
11148
        virBufferAddLit(&buf, "  <target>\n");
11149
        virBufferAsprintf(&buf, "    <format type='%s'/>\n",format);
11150
        virBufferAddLit(&buf, "  </target>\n");
11151
    }
11152

11153 11154 11155 11156
    /* Convert the snapshot parameters into backingStore XML */
    if (snapshotStrVol) {
        /* Lookup snapshot backing volume.  Try the backing-vol
         *  parameter as a name */
11157 11158
        vshDebug(ctl, VSH_ERR_DEBUG,
                 "%s: Look up backing store volume '%s' as name\n",
11159 11160 11161
                 cmd->def->name, snapshotStrVol);
        virStorageVolPtr snapVol = virStorageVolLookupByName(pool, snapshotStrVol);
        if (snapVol)
11162 11163
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as name\n",
11164 11165 11166 11167 11168
                         cmd->def->name, snapshotStrVol);

        if (snapVol == NULL) {
            /* Snapshot backing volume not found by name.  Try the
             *  backing-vol parameter as a key */
11169 11170
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: Look up backing store volume '%s' as key\n",
11171 11172 11173
                     cmd->def->name, snapshotStrVol);
            snapVol = virStorageVolLookupByKey(ctl->conn, snapshotStrVol);
            if (snapVol)
11174 11175
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as key\n",
11176 11177 11178 11179 11180
                         cmd->def->name, snapshotStrVol);
        }
        if (snapVol == NULL) {
            /* Snapshot backing volume not found by key.  Try the
             *  backing-vol parameter as a path */
11181 11182
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: Look up backing store volume '%s' as path\n",
11183 11184 11185
                     cmd->def->name, snapshotStrVol);
            snapVol = virStorageVolLookupByPath(ctl->conn, snapshotStrVol);
            if (snapVol)
11186 11187
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as path\n",
11188 11189 11190 11191
                         cmd->def->name, snapshotStrVol);
        }
        if (snapVol == NULL) {
            vshError(ctl, _("failed to get vol '%s'"), snapshotStrVol);
11192
            goto cleanup;
11193 11194 11195 11196 11197
        }

        char *snapshotStrVolPath;
        if ((snapshotStrVolPath = virStorageVolGetPath(snapVol)) == NULL) {
            virStorageVolFree(snapVol);
11198
            goto cleanup;
11199 11200 11201 11202
        }

        /* Create XML for the backing store */
        virBufferAddLit(&buf, "  <backingStore>\n");
11203
        virBufferAsprintf(&buf, "    <path>%s</path>\n",snapshotStrVolPath);
11204
        if (snapshotStrFormat)
11205
            virBufferAsprintf(&buf, "    <format type='%s'/>\n",snapshotStrFormat);
11206 11207 11208 11209 11210 11211 11212 11213
        virBufferAddLit(&buf, "  </backingStore>\n");

        /* Cleanup snapshot allocations */
        VIR_FREE(snapshotStrVolPath);
        virStorageVolFree(snapVol);
    }

    virBufferAddLit(&buf, "</volume>\n");
11214

11215 11216
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
11217
        goto cleanup;
11218 11219 11220
    }
    xml = virBufferContentAndReset(&buf);
    vol = virStorageVolCreateXML(pool, xml, 0);
11221
    VIR_FREE(xml);
11222 11223 11224 11225 11226
    virStoragePoolFree(pool);

    if (vol != NULL) {
        vshPrint(ctl, _("Vol %s created\n"), name);
        virStorageVolFree(vol);
E
Eric Blake 已提交
11227
        return true;
11228
    } else {
11229
        vshError(ctl, _("Failed to create vol %s"), name);
E
Eric Blake 已提交
11230
        return false;
11231 11232 11233
    }

 cleanup:
11234
    virBufferFreeAndReset(&buf);
11235
    virStoragePoolFree(pool);
E
Eric Blake 已提交
11236
    return false;
11237 11238 11239
}


11240 11241 11242
/*
 * "pool-undefine" command
 */
11243
static const vshCmdInfo info_pool_undefine[] = {
11244 11245
    {"help", N_("undefine an inactive pool")},
    {"desc", N_("Undefine the configuration for an inactive pool.")},
11246 11247 11248
    {NULL, NULL}
};

11249
static const vshCmdOptDef opts_pool_undefine[] = {
11250
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
11251 11252 11253
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11254
static bool
11255
cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd)
11256 11257
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
11258
    bool ret = true;
11259
    const char *name;
11260

11261
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11262
        return false;
11263 11264

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
11265
        return false;
11266 11267 11268 11269

    if (virStoragePoolUndefine(pool) == 0) {
        vshPrint(ctl, _("Pool %s has been undefined\n"), name);
    } else {
11270
        vshError(ctl, _("Failed to undefine pool %s"), name);
E
Eric Blake 已提交
11271
        ret = false;
11272 11273
    }

L
Laine Stump 已提交
11274
    virStoragePoolFree(pool);
11275 11276 11277 11278 11279 11280 11281
    return ret;
}


/*
 * "pool-uuid" command
 */
11282
static const vshCmdInfo info_pool_uuid[] = {
11283
    {"help", N_("convert a pool name to pool UUID")},
11284
    {"desc", ""},
11285 11286 11287
    {NULL, NULL}
};

11288
static const vshCmdOptDef opts_pool_uuid[] = {
11289
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
11290 11291 11292
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11293
static bool
11294
cmdPoolUuid(vshControl *ctl, const vshCmd *cmd)
11295 11296 11297 11298
{
    virStoragePoolPtr pool;
    char uuid[VIR_UUID_STRING_BUFLEN];

11299
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11300
        return false;
11301 11302

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
11303
                                           VSH_BYNAME)))
E
Eric Blake 已提交
11304
        return false;
11305 11306 11307 11308

    if (virStoragePoolGetUUIDString(pool, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
11309
        vshError(ctl, "%s", _("failed to get pool UUID"));
11310

L
Laine Stump 已提交
11311
    virStoragePoolFree(pool);
E
Eric Blake 已提交
11312
    return true;
11313 11314 11315 11316 11317 11318
}


/*
 * "vol-create" command
 */
11319
static const vshCmdInfo info_vol_create[] = {
11320 11321
    {"help", N_("create a vol from an XML file")},
    {"desc", N_("Create a vol.")},
11322 11323 11324
    {NULL, NULL}
};

11325
static const vshCmdOptDef opts_vol_create[] = {
11326 11327
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
11328 11329 11330
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11331
static bool
11332
cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
11333 11334 11335
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
11336
    const char *from = NULL;
E
Eric Blake 已提交
11337
    bool ret = true;
11338 11339
    char *buffer;

11340
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11341
        return false;
11342 11343

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
11344
                                           VSH_BYNAME)))
E
Eric Blake 已提交
11345
        return false;
11346

11347
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
11348
        virStoragePoolFree(pool);
E
Eric Blake 已提交
11349
        return false;
11350 11351 11352
    }

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
11353
        virshReportError(ctl);
11354
        virStoragePoolFree(pool);
E
Eric Blake 已提交
11355
        return false;
11356 11357 11358
    }

    vol = virStorageVolCreateXML(pool, buffer, 0);
11359
    VIR_FREE(buffer);
11360 11361 11362 11363 11364 11365 11366
    virStoragePoolFree(pool);

    if (vol != NULL) {
        vshPrint(ctl, _("Vol %s created from %s\n"),
                 virStorageVolGetName(vol), from);
        virStorageVolFree(vol);
    } else {
11367
        vshError(ctl, _("Failed to create vol from %s"), from);
E
Eric Blake 已提交
11368
        ret = false;
11369 11370 11371 11372
    }
    return ret;
}

11373 11374 11375 11376
/*
 * "vol-create-from" command
 */
static const vshCmdInfo info_vol_create_from[] = {
11377 11378
    {"help", N_("create a vol, using another volume as input")},
    {"desc", N_("Create a vol from an existing volume.")},
11379 11380 11381 11382
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_create_from[] = {
11383 11384 11385
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("input vol name or key")},
E
Eric Blake 已提交
11386
    {"inputpool", VSH_OT_STRING, 0, N_("pool name or uuid of the input volume's pool")},
11387 11388 11389
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11390
static bool
11391 11392 11393 11394
cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool = NULL;
    virStorageVolPtr newvol = NULL, inputvol = NULL;
11395
    const char *from = NULL;
E
Eric Blake 已提交
11396
    bool ret = false;
11397 11398
    char *buffer = NULL;

11399
    if (!vshConnectionUsability(ctl, ctl->conn))
11400 11401 11402 11403 11404
        goto cleanup;

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME)))
        goto cleanup;

11405
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
11406 11407 11408 11409 11410 11411 11412
        goto cleanup;
    }

    if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL)))
        goto cleanup;

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
11413
        virshReportError(ctl);
11414 11415 11416 11417 11418 11419 11420 11421 11422
        goto cleanup;
    }

    newvol = virStorageVolCreateXMLFrom(pool, buffer, inputvol, 0);

    if (newvol != NULL) {
        vshPrint(ctl, _("Vol %s created from input vol %s\n"),
                 virStorageVolGetName(newvol), virStorageVolGetName(inputvol));
    } else {
11423
        vshError(ctl, _("Failed to create vol from %s"), from);
11424 11425 11426
        goto cleanup;
    }

E
Eric Blake 已提交
11427
    ret = true;
11428
cleanup:
11429
    VIR_FREE(buffer);
11430 11431 11432 11433
    if (pool)
        virStoragePoolFree(pool);
    if (inputvol)
        virStorageVolFree(inputvol);
L
Laine Stump 已提交
11434 11435
    if (newvol)
        virStorageVolFree(newvol);
11436 11437 11438 11439
    return ret;
}

static xmlChar *
11440 11441
makeCloneXML(const char *origxml, const char *newname)
{
11442

11443 11444 11445
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
11446 11447 11448
    xmlChar *newxml = NULL;
    int size;

11449
    doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
11450 11451 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471
    if (!doc)
        goto cleanup;

    obj = xmlXPathEval(BAD_CAST "/volume/name", ctxt);
    if ((obj == NULL) || (obj->nodesetval == NULL) ||
        (obj->nodesetval->nodeTab == NULL))
        goto cleanup;

    xmlNodeSetContent(obj->nodesetval->nodeTab[0], (const xmlChar *)newname);
    xmlDocDumpMemory(doc, &newxml, &size);

cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(doc);
    return newxml;
}

/*
 * "vol-clone" command
 */
static const vshCmdInfo info_vol_clone[] = {
11472 11473
    {"help", N_("clone a volume.")},
    {"desc", N_("Clone an existing volume.")},
11474 11475 11476 11477
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_clone[] = {
11478 11479
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("orig vol name or key")},
    {"newname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("clone name")},
E
Eric Blake 已提交
11480
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11481 11482 11483
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11484
static bool
11485 11486 11487 11488
cmdVolClone(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr origpool = NULL;
    virStorageVolPtr origvol = NULL, newvol = NULL;
11489
    const char *name = NULL;
11490
    char *origxml = NULL;
11491
    xmlChar *newxml = NULL;
E
Eric Blake 已提交
11492
    bool ret = false;
11493

11494
    if (!vshConnectionUsability(ctl, ctl->conn))
11495 11496 11497 11498 11499 11500 11501
        goto cleanup;

    if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
        goto cleanup;

    origpool = virStoragePoolLookupByVolume(origvol);
    if (!origpool) {
11502
        vshError(ctl, "%s", _("failed to get parent pool"));
11503 11504 11505
        goto cleanup;
    }

11506
    if (vshCommandOptString(cmd, "newname", &name) <= 0)
11507 11508 11509 11510 11511 11512 11513 11514 11515 11516 11517 11518 11519 11520 11521 11522 11523 11524
        goto cleanup;

    origxml = virStorageVolGetXMLDesc(origvol, 0);
    if (!origxml)
        goto cleanup;

    newxml = makeCloneXML(origxml, name);
    if (!newxml) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
        goto cleanup;
    }

    newvol = virStorageVolCreateXMLFrom(origpool, (char *) newxml, origvol, 0);

    if (newvol != NULL) {
        vshPrint(ctl, _("Vol %s cloned from %s\n"),
                 virStorageVolGetName(newvol), virStorageVolGetName(origvol));
    } else {
11525
        vshError(ctl, _("Failed to clone vol from %s"),
11526 11527 11528 11529
                 virStorageVolGetName(origvol));
        goto cleanup;
    }

E
Eric Blake 已提交
11530
    ret = true;
11531 11532

cleanup:
11533
    VIR_FREE(origxml);
11534 11535 11536
    xmlFree(newxml);
    if (origvol)
        virStorageVolFree(origvol);
L
Laine Stump 已提交
11537 11538
    if (newvol)
        virStorageVolFree(newvol);
11539 11540 11541 11542 11543
    if (origpool)
        virStoragePoolFree(origpool);
    return ret;
}

11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556

/*
 * "vol-upload" command
 */
static const vshCmdInfo info_vol_upload[] = {
    {"help", N_("upload a file into a volume")},
    {"desc", N_("Upload a file into a volume")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_upload[] = {
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
E
Eric Blake 已提交
11557
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571
    {"offset", VSH_OT_INT, 0, N_("volume offset to upload to") },
    {"length", VSH_OT_INT, 0, N_("amount of data to upload") },
    {NULL, 0, 0, NULL}
};

static int
cmdVolUploadSource(virStreamPtr st ATTRIBUTE_UNUSED,
                   char *bytes, size_t nbytes, void *opaque)
{
    int *fd = opaque;

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

E
Eric Blake 已提交
11572
static bool
11573 11574 11575 11576
cmdVolUpload (vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    virStorageVolPtr vol = NULL;
E
Eric Blake 已提交
11577
    bool ret = false;
11578 11579 11580 11581 11582 11583 11584 11585 11586 11587
    int fd = -1;
    virStreamPtr st = NULL;
    const char *name = NULL;
    unsigned long long offset = 0, length = 0;

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

    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
11588
        return false;
11589 11590 11591 11592
    }

    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
11593
        return false;
11594 11595 11596
    }

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
11597
        return false;
11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 11625 11626 11627 11628 11629 11630 11631
    }

    if (vshCommandOptString(cmd, "file", &file) < 0) {
        vshError(ctl, _("file must not be empty"));
        goto cleanup;
    }

    if ((fd = open(file, O_RDONLY)) < 0) {
        vshError(ctl, _("cannot read %s"), file);
        goto cleanup;
    }

    st = virStreamNew(ctl->conn, 0);
    if (virStorageVolUpload(vol, st, offset, length, 0) < 0) {
        vshError(ctl, _("cannot upload to volume %s"), name);
        goto cleanup;
    }

    if (virStreamSendAll(st, cmdVolUploadSource, &fd) < 0) {
        vshError(ctl, _("cannot send data to volume %s"), name);
        goto cleanup;
    }

    if (VIR_CLOSE(fd) < 0) {
        vshError(ctl, _("cannot close file %s"), file);
        virStreamAbort(st);
        goto cleanup;
    }

    if (virStreamFinish(st) < 0) {
        vshError(ctl, _("cannot close volume %s"), name);
        goto cleanup;
    }

E
Eric Blake 已提交
11632
    ret = true;
11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656

cleanup:
    if (vol)
        virStorageVolFree(vol);
    if (st)
        virStreamFree(st);
    VIR_FORCE_CLOSE(fd);
    return ret;
}



/*
 * "vol-download" command
 */
static const vshCmdInfo info_vol_download[] = {
    {"help", N_("Download a volume to a file")},
    {"desc", N_("Download a volume to a file")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_download[] = {
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
E
Eric Blake 已提交
11657
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11658 11659 11660 11661 11662
    {"offset", VSH_OT_INT, 0, N_("volume offset to download from") },
    {"length", VSH_OT_INT, 0, N_("amount of data to download") },
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11663
static bool
11664 11665 11666 11667
cmdVolDownload (vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    virStorageVolPtr vol = NULL;
E
Eric Blake 已提交
11668
    bool ret = false;
11669 11670 11671 11672
    int fd = -1;
    virStreamPtr st = NULL;
    const char *name = NULL;
    unsigned long long offset = 0, length = 0;
11673
    bool created = false;
11674 11675

    if (!vshConnectionUsability(ctl, ctl->conn))
11676
        return false;
11677 11678 11679

    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
11680
        return false;
11681 11682 11683 11684
    }

    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
11685
        return false;
11686 11687 11688
    }

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name)))
E
Eric Blake 已提交
11689
        return false;
11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701

    if (vshCommandOptString(cmd, "file", &file) < 0) {
        vshError(ctl, _("file must not be empty"));
        goto cleanup;
    }

    if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
        if (errno != EEXIST ||
            (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
            vshError(ctl, _("cannot create %s"), file);
            goto cleanup;
        }
11702 11703
    } else {
        created = true;
11704 11705 11706 11707 11708 11709 11710 11711
    }

    st = virStreamNew(ctl->conn, 0);
    if (virStorageVolDownload(vol, st, offset, length, 0) < 0) {
        vshError(ctl, _("cannot download from volume %s"), name);
        goto cleanup;
    }

11712
    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727
        vshError(ctl, _("cannot receive data from volume %s"), name);
        goto cleanup;
    }

    if (VIR_CLOSE(fd) < 0) {
        vshError(ctl, _("cannot close file %s"), file);
        virStreamAbort(st);
        goto cleanup;
    }

    if (virStreamFinish(st) < 0) {
        vshError(ctl, _("cannot close volume %s"), name);
        goto cleanup;
    }

E
Eric Blake 已提交
11728
    ret = true;
11729 11730

cleanup:
11731
    VIR_FORCE_CLOSE(fd);
11732
    if (!ret && created)
11733 11734 11735 11736 11737 11738 11739 11740 11741
        unlink(file);
    if (vol)
        virStorageVolFree(vol);
    if (st)
        virStreamFree(st);
    return ret;
}


11742 11743 11744
/*
 * "vol-delete" command
 */
11745
static const vshCmdInfo info_vol_delete[] = {
11746 11747
    {"help", N_("delete a vol")},
    {"desc", N_("Delete a given vol.")},
11748 11749 11750
    {NULL, NULL}
};

11751
static const vshCmdOptDef opts_vol_delete[] = {
11752
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
11753
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11754 11755 11756
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11757
static bool
11758
cmdVolDelete(vshControl *ctl, const vshCmd *cmd)
11759 11760
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
11761
    bool ret = true;
11762
    const char *name;
11763

11764
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11765
        return false;
11766 11767

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
11768
        return false;
11769 11770 11771
    }

    if (virStorageVolDelete(vol, 0) == 0) {
D
Daniel Veillard 已提交
11772
        vshPrint(ctl, _("Vol %s deleted\n"), name);
11773
    } else {
11774
        vshError(ctl, _("Failed to delete vol %s"), name);
E
Eric Blake 已提交
11775
        ret = false;
11776 11777
    }

L
Laine Stump 已提交
11778
    virStorageVolFree(vol);
11779 11780 11781 11782
    return ret;
}


D
David Allan 已提交
11783 11784 11785 11786 11787 11788 11789 11790 11791 11792 11793
/*
 * "vol-wipe" command
 */
static const vshCmdInfo info_vol_wipe[] = {
    {"help", N_("wipe a vol")},
    {"desc", N_("Ensure data previously on a volume is not accessible to future reads")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_wipe[] = {
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
11794
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11795
    {"algorithm", VSH_OT_STRING, 0, N_("perform selected wiping algorithm")},
D
David Allan 已提交
11796 11797 11798
    {NULL, 0, 0, NULL}
};

11799 11800 11801 11802 11803
VIR_ENUM_DECL(virStorageVolWipeAlgorithm)
VIR_ENUM_IMPL(virStorageVolWipeAlgorithm, VIR_STORAGE_VOL_WIPE_ALG_LAST,
              "zero", "nnsa", "dod", "bsi", "gutmann", "schneier",
              "pfitzner7", "pfitzner33", "random");

E
Eric Blake 已提交
11804
static bool
D
David Allan 已提交
11805 11806 11807
cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
{
    virStorageVolPtr vol;
11808
    bool ret = false;
11809
    const char *name;
11810 11811 11812
    const char *algorithm_str = NULL;
    int algorithm = VIR_STORAGE_VOL_WIPE_ALG_ZERO;
    int funcRet;
D
David Allan 已提交
11813

11814
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11815
        return false;
D
David Allan 已提交
11816 11817

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
11818
        return false;
D
David Allan 已提交
11819 11820
    }

11821 11822 11823 11824 11825 11826 11827 11828 11829 11830 11831 11832 11833 11834 11835 11836 11837 11838
    if (vshCommandOptString(cmd, "algorithm", &algorithm_str) < 0) {
        vshError(ctl, "%s", _("missing argument"));
        goto out;
    }

    if (algorithm_str &&
        (algorithm = virStorageVolWipeAlgorithmTypeFromString(algorithm_str)) < 0) {
        vshError(ctl, _("Unsupported algorithm '%s'"), algorithm_str);
        goto out;
    }

    if ((funcRet = virStorageVolWipePattern(vol, algorithm, 0)) < 0) {
        if (last_error->code == VIR_ERR_NO_SUPPORT &&
            algorithm == VIR_STORAGE_VOL_WIPE_ALG_ZERO)
            funcRet = virStorageVolWipe(vol, 0);
    }

    if (funcRet < 0) {
D
David Allan 已提交
11839
        vshError(ctl, _("Failed to wipe vol %s"), name);
11840
        goto out;
D
David Allan 已提交
11841 11842
    }

11843 11844 11845
    vshPrint(ctl, _("Vol %s wiped\n"), name);
    ret = true;
out:
D
David Allan 已提交
11846 11847 11848 11849 11850
    virStorageVolFree(vol);
    return ret;
}


11851 11852 11853
/*
 * "vol-info" command
 */
11854
static const vshCmdInfo info_vol_info[] = {
11855 11856
    {"help", N_("storage vol information")},
    {"desc", N_("Returns basic information about the storage vol.")},
11857 11858 11859
    {NULL, NULL}
};

11860
static const vshCmdOptDef opts_vol_info[] = {
11861
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
11862
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11863 11864 11865
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11866
static bool
11867
cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
11868 11869 11870
{
    virStorageVolInfo info;
    virStorageVolPtr vol;
E
Eric Blake 已提交
11871
    bool ret = true;
11872

11873
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11874
        return false;
11875 11876

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
11877
        return false;
11878 11879 11880 11881 11882 11883

    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol));

    if (virStorageVolGetInfo(vol, &info) == 0) {
        double val;
        const char *unit;
R
Ryota Ozaki 已提交
11884 11885 11886 11887 11888 11889 11890 11891 11892 11893 11894 11895 11896 11897 11898 11899
        switch(info.type) {
        case VIR_STORAGE_VOL_FILE:
            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("file"));
            break;

        case VIR_STORAGE_VOL_BLOCK:
            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("block"));
            break;

        case VIR_STORAGE_VOL_DIR:
            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("dir"));
            break;

        default:
            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("unknown"));
        }
11900 11901 11902 11903 11904 11905 11906

        val = prettyCapacity(info.capacity, &unit);
        vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);

        val = prettyCapacity(info.allocation, &unit);
        vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
    } else {
E
Eric Blake 已提交
11907
        ret = false;
11908 11909 11910 11911 11912 11913
    }

    virStorageVolFree(vol);
    return ret;
}

11914 11915 11916 11917 11918 11919 11920 11921 11922 11923 11924 11925
/*
 * "vol-resize" command
 */
static const vshCmdInfo info_vol_resize[] = {
    {"help", N_("resize a vol")},
    {"desc", N_("Resizes a storage volume.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_resize[] = {
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
E
Eric Blake 已提交
11926
     N_("new capacity for the vol, as scaled integer (default bytes)")},
11927 11928 11929 11930 11931 11932 11933 11934 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
    {"allocate", VSH_OT_BOOL, 0,
     N_("allocate the new capacity, rather than leaving it sparse")},
    {"delta", VSH_OT_BOOL, 0,
     N_("use capacity as a delta to current size, rather than the new size")},
    {"shrink", VSH_OT_BOOL, 0, N_("allow the resize to shrink the volume")},
    {NULL, 0, 0, NULL}
};

static bool
cmdVolResize(vshControl *ctl, const vshCmd *cmd)
{
    virStorageVolPtr vol;
    const char *capacityStr = NULL;
    unsigned long long capacity = 0;
    unsigned int flags = 0;
    bool ret = false;
    bool delta = false;

    if (vshCommandOptBool(cmd, "allocate"))
        flags |= VIR_STORAGE_VOL_RESIZE_ALLOCATE;
    if (vshCommandOptBool(cmd, "delta")) {
        delta = true;
        flags |= VIR_STORAGE_VOL_RESIZE_DELTA;
    }
    if (vshCommandOptBool(cmd, "shrink"))
        flags |= VIR_STORAGE_VOL_RESIZE_SHRINK;

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

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
        return false;

    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
        goto cleanup;
E
Eric Blake 已提交
11963 11964 11965 11966 11967 11968 11969 11970 11971
    virSkipSpaces(&capacityStr);
    if (*capacityStr == '-') {
        /* The API always requires a positive value; but we allow a
         * negative value for convenience.  */
        if (delta && vshCommandOptBool(cmd, "shrink")){
            capacityStr++;
        } else {
            vshError(ctl, "%s",
                     _("negative size requires --delta and --shrink"));
11972 11973 11974
            goto cleanup;
        }
    }
E
Eric Blake 已提交
11975 11976 11977 11978
    if (vshVolSize(capacityStr, &capacity) < 0) {
        vshError(ctl, _("Malformed size %s"), capacityStr);
        goto cleanup;
    }
11979 11980 11981 11982 11983 11984 11985 11986 11987 11988 11989 11990 11991 11992 11993 11994 11995 11996 11997 11998

    if (virStorageVolResize(vol, capacity, flags) == 0) {
        vshPrint(ctl,
                 delta ? _("Size of volume '%s' successfully changed by %s\n")
                 : _("Size of volume '%s' successfully changed to %s\n"),
                 virStorageVolGetName(vol), capacityStr);
        ret = true;
    } else {
        vshError(ctl,
                 delta ? _("Failed to change size of volume '%s' by %s\n")
                 : _("Failed to change size of volume '%s' to %s\n"),
                 virStorageVolGetName(vol), capacityStr);
        ret = false;
    }

cleanup:
    virStorageVolFree(vol);
    return ret;
}

11999 12000 12001 12002

/*
 * "vol-dumpxml" command
 */
12003
static const vshCmdInfo info_vol_dumpxml[] = {
12004 12005
    {"help", N_("vol information in XML")},
    {"desc", N_("Output the vol information as an XML dump to stdout.")},
12006 12007 12008
    {NULL, NULL}
};

12009
static const vshCmdOptDef opts_vol_dumpxml[] = {
12010
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
12011
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
12012 12013 12014
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12015
static bool
12016
cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
12017 12018
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
12019
    bool ret = true;
12020 12021
    char *dump;

12022
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12023
        return false;
12024 12025

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
12026
        return false;
12027 12028 12029

    dump = virStorageVolGetXMLDesc(vol, 0);
    if (dump != NULL) {
12030
        vshPrint(ctl, "%s", dump);
12031
        VIR_FREE(dump);
12032
    } else {
E
Eric Blake 已提交
12033
        ret = false;
12034 12035 12036 12037 12038 12039 12040 12041 12042 12043
    }

    virStorageVolFree(vol);
    return ret;
}


/*
 * "vol-list" command
 */
12044
static const vshCmdInfo info_vol_list[] = {
12045 12046
    {"help", N_("list vols")},
    {"desc", N_("Returns list of vols by pool.")},
12047 12048 12049
    {NULL, NULL}
};

12050
static const vshCmdOptDef opts_vol_list[] = {
12051
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
12052
    {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")},
12053 12054 12055
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12056
static bool
12057
cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
12058
{
12059
    virStorageVolInfo volumeInfo;
12060 12061
    virStoragePoolPtr pool;
    char **activeNames = NULL;
12062 12063 12064 12065 12066
    char *outputStr = NULL;
    const char *unit;
    double val;
    int details = vshCommandOptBool(cmd, "details");
    int numVolumes = 0, i;
W
Wen Congyang 已提交
12067 12068
    int ret;
    bool functionReturn;
12069 12070 12071 12072 12073 12074 12075 12076 12077 12078 12079
    int stringLength = 0;
    size_t allocStrLength = 0, capStrLength = 0;
    size_t nameStrLength = 0, pathStrLength = 0;
    size_t typeStrLength = 0;
    struct volInfoText {
        char *allocation;
        char *capacity;
        char *path;
        char *type;
    };
    struct volInfoText *volInfoTexts = NULL;
12080

12081
    /* Check the connection to libvirtd daemon is still working */
12082
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12083
        return false;
12084

12085
    /* Look up the pool information given to us by the user */
12086
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
12087
        return false;
12088

12089 12090
    /* Determine the number of volumes in the pool */
    numVolumes = virStoragePoolNumOfVolumes(pool);
12091

12092 12093 12094
    if (numVolumes < 0) {
        vshError(ctl, "%s", _("Failed to list storage volumes"));
        virStoragePoolFree(pool);
E
Eric Blake 已提交
12095
        return false;
12096 12097
    }

12098 12099 12100 12101 12102
    /* Retrieve the list of volume names in the pool */
    if (numVolumes > 0) {
        activeNames = vshCalloc(ctl, numVolumes, sizeof(*activeNames));
        if ((numVolumes = virStoragePoolListVolumes(pool, activeNames,
                                                    numVolumes)) < 0) {
12103
            vshError(ctl, "%s", _("Failed to list active vols"));
12104
            VIR_FREE(activeNames);
12105
            virStoragePoolFree(pool);
E
Eric Blake 已提交
12106
            return false;
12107 12108
        }

12109 12110 12111 12112 12113
        /* Sort the volume names */
        qsort(&activeNames[0], numVolumes, sizeof(*activeNames), namesorter);

        /* Set aside memory for volume information pointers */
        volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts));
12114 12115
    }

12116 12117 12118 12119 12120
    /* Collect the rest of the volume information for display */
    for (i = 0; i < numVolumes; i++) {
        /* Retrieve volume info */
        virStorageVolPtr vol = virStorageVolLookupByName(pool,
                                                         activeNames[i]);
12121

12122 12123 12124 12125
        /* Retrieve the volume path */
        if ((volInfoTexts[i].path = virStorageVolGetPath(vol)) == NULL) {
            /* Something went wrong retrieving a volume path, cope with it */
            volInfoTexts[i].path = vshStrdup(ctl, _("unknown"));
12126 12127
        }

12128 12129 12130 12131 12132 12133 12134 12135 12136
        /* If requested, retrieve volume type and sizing information */
        if (details) {
            if (virStorageVolGetInfo(vol, &volumeInfo) != 0) {
                /* Something went wrong retrieving volume info, cope with it */
                volInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
                volInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
                volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
            } else {
                /* Convert the returned volume info into output strings */
12137

12138
                /* Volume type */
12139 12140 12141 12142 12143 12144 12145 12146 12147 12148 12149 12150 12151
                switch (volumeInfo.type) {
                        case VIR_STORAGE_VOL_FILE:
                            volInfoTexts[i].type = vshStrdup(ctl, _("file"));
                            break;
                        case VIR_STORAGE_VOL_BLOCK:
                            volInfoTexts[i].type = vshStrdup(ctl, _("block"));
                            break;
                        case VIR_STORAGE_VOL_DIR:
                            volInfoTexts[i].type = vshStrdup(ctl, _("dir"));
                            break;
                        default:
                            volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
                }
12152 12153 12154 12155 12156 12157 12158 12159 12160 12161 12162 12163 12164 12165 12166 12167 12168 12169 12170 12171 12172 12173 12174 12175 12176 12177 12178 12179 12180 12181 12182 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 12193 12194 12195 12196 12197 12198 12199 12200 12201

                /* Create the capacity output string */
                val = prettyCapacity(volumeInfo.capacity, &unit);
                ret = virAsprintf(&volInfoTexts[i].capacity,
                                  "%.2lf %s", val, unit);
                if (ret < 0) {
                    /* An error occurred creating the string, return */
                    goto asprintf_failure;
                }

                /* Create the allocation output string */
                val = prettyCapacity(volumeInfo.allocation, &unit);
                ret = virAsprintf(&volInfoTexts[i].allocation,
                                  "%.2lf %s", val, unit);
                if (ret < 0) {
                    /* An error occurred creating the string, return */
                    goto asprintf_failure;
                }
            }

            /* Remember the largest length for each output string.
             * This lets us displaying header and volume information rows
             * using a single, properly sized, printf style output string.
             */

            /* Keep the length of name string if longest so far */
            stringLength = strlen(activeNames[i]);
            if (stringLength > nameStrLength)
                nameStrLength = stringLength;

            /* Keep the length of path string if longest so far */
            stringLength = strlen(volInfoTexts[i].path);
            if (stringLength > pathStrLength)
                pathStrLength = stringLength;

            /* Keep the length of type string if longest so far */
            stringLength = strlen(volInfoTexts[i].type);
            if (stringLength > typeStrLength)
                typeStrLength = stringLength;

            /* Keep the length of capacity string if longest so far */
            stringLength = strlen(volInfoTexts[i].capacity);
            if (stringLength > capStrLength)
                capStrLength = stringLength;

            /* Keep the length of allocation string if longest so far */
            stringLength = strlen(volInfoTexts[i].allocation);
            if (stringLength > allocStrLength)
                allocStrLength = stringLength;
        }
12202

12203
        /* Cleanup memory allocation */
12204
        virStorageVolFree(vol);
12205 12206 12207 12208 12209 12210 12211 12212 12213 12214 12215 12216 12217 12218 12219 12220 12221 12222
    }

    /* If the --details option wasn't selected, we output the volume
     * info using the fixed string format from previous versions to
     * maintain backward compatibility.
     */

    /* Output basic info then return if --details option not selected */
    if (!details) {
        /* The old output format */
        vshPrintExtra(ctl, "%-20s %-40s\n", _("Name"), _("Path"));
        vshPrintExtra(ctl, "-----------------------------------------\n");
        for (i = 0; i < numVolumes; i++) {
            vshPrint(ctl, "%-20s %-40s\n", activeNames[i],
                     volInfoTexts[i].path);
        }

        /* Cleanup and return */
E
Eric Blake 已提交
12223
        functionReturn = true;
12224 12225 12226 12227 12228 12229 12230 12231 12232 12233 12234 12235 12236 12237 12238 12239 12240 12241 12242 12243 12244 12245 12246 12247 12248 12249 12250 12251 12252 12253 12254
        goto cleanup;
    }

    /* We only get here if the --details option was selected. */

    /* Use the length of name header string if it's longest */
    stringLength = strlen(_("Name"));
    if (stringLength > nameStrLength)
        nameStrLength = stringLength;

    /* Use the length of path header string if it's longest */
    stringLength = strlen(_("Path"));
    if (stringLength > pathStrLength)
        pathStrLength = stringLength;

    /* Use the length of type header string if it's longest */
    stringLength = strlen(_("Type"));
    if (stringLength > typeStrLength)
        typeStrLength = stringLength;

    /* Use the length of capacity header string if it's longest */
    stringLength = strlen(_("Capacity"));
    if (stringLength > capStrLength)
        capStrLength = stringLength;

    /* Use the length of allocation header string if it's longest */
    stringLength = strlen(_("Allocation"));
    if (stringLength > allocStrLength)
        allocStrLength = stringLength;

    /* Display the string lengths for debugging */
12255 12256 12257 12258 12259 12260 12261 12262 12263 12264
    vshDebug(ctl, VSH_ERR_DEBUG,
             "Longest name string = %zu chars\n", nameStrLength);
    vshDebug(ctl, VSH_ERR_DEBUG,
             "Longest path string = %zu chars\n", pathStrLength);
    vshDebug(ctl, VSH_ERR_DEBUG,
             "Longest type string = %zu chars\n", typeStrLength);
    vshDebug(ctl, VSH_ERR_DEBUG,
             "Longest capacity string = %zu chars\n", capStrLength);
    vshDebug(ctl, VSH_ERR_DEBUG,
             "Longest allocation string = %zu chars\n", allocStrLength);
12265 12266 12267 12268 12269 12270 12271 12272 12273 12274 12275 12276 12277 12278 12279 12280 12281 12282 12283 12284 12285 12286 12287 12288 12289 12290 12291 12292 12293 12294 12295 12296 12297 12298

    /* Create the output template */
    ret = virAsprintf(&outputStr,
                      "%%-%lus  %%-%lus  %%-%lus  %%%lus  %%%lus\n",
                      (unsigned long) nameStrLength,
                      (unsigned long) pathStrLength,
                      (unsigned long) typeStrLength,
                      (unsigned long) capStrLength,
                      (unsigned long) allocStrLength);
    if (ret < 0) {
        /* An error occurred creating the string, return */
        goto asprintf_failure;
    }

    /* Display the header */
    vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"),
             ("Capacity"), _("Allocation"));
    for (i = nameStrLength + pathStrLength + typeStrLength
                           + capStrLength + allocStrLength
                           + 8; i > 0; i--)
        vshPrintExtra(ctl, "-");
    vshPrintExtra(ctl, "\n");

    /* Display the volume info rows */
    for (i = 0; i < numVolumes; i++) {
        vshPrint(ctl, outputStr,
                 activeNames[i],
                 volInfoTexts[i].path,
                 volInfoTexts[i].type,
                 volInfoTexts[i].capacity,
                 volInfoTexts[i].allocation);
    }

    /* Cleanup and return */
E
Eric Blake 已提交
12299
    functionReturn = true;
12300 12301 12302 12303 12304 12305 12306 12307 12308 12309 12310 12311 12312 12313
    goto cleanup;

asprintf_failure:

    /* Display an appropriate error message then cleanup and return */
    switch (errno) {
    case ENOMEM:
        /* Couldn't allocate memory */
        vshError(ctl, "%s", _("Out of memory"));
        break;
    default:
        /* Some other error */
        vshError(ctl, _("virAsprintf failed (errno %d)"), errno);
    }
E
Eric Blake 已提交
12314
    functionReturn = false;
12315 12316 12317 12318 12319 12320 12321 12322 12323 12324

cleanup:

    /* Safely free the memory allocated in this function */
    for (i = 0; i < numVolumes; i++) {
        /* Cleanup the memory for one volume info structure per loop */
        VIR_FREE(volInfoTexts[i].path);
        VIR_FREE(volInfoTexts[i].type);
        VIR_FREE(volInfoTexts[i].capacity);
        VIR_FREE(volInfoTexts[i].allocation);
12325
        VIR_FREE(activeNames[i]);
12326
    }
12327 12328 12329 12330

    /* Cleanup remaining memory */
    VIR_FREE(outputStr);
    VIR_FREE(volInfoTexts);
12331
    VIR_FREE(activeNames);
12332
    virStoragePoolFree(pool);
12333 12334 12335

    /* Return the desired value */
    return functionReturn;
12336 12337 12338 12339 12340 12341
}


/*
 * "vol-name" command
 */
12342
static const vshCmdInfo info_vol_name[] = {
12343
    {"help", N_("returns the volume name for a given volume key or path")},
12344
    {"desc", ""},
12345 12346 12347
    {NULL, NULL}
};

12348
static const vshCmdOptDef opts_vol_name[] = {
12349
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
12350 12351 12352
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12353
static bool
12354
cmdVolName(vshControl *ctl, const vshCmd *cmd)
12355 12356 12357
{
    virStorageVolPtr vol;

12358
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12359
        return false;
12360

12361
    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
12362
                                   VSH_BYUUID)))
E
Eric Blake 已提交
12363
        return false;
12364 12365 12366

    vshPrint(ctl, "%s\n", virStorageVolGetName(vol));
    virStorageVolFree(vol);
E
Eric Blake 已提交
12367
    return true;
12368 12369 12370
}


J
Justin Clift 已提交
12371 12372 12373 12374 12375 12376 12377 12378 12379 12380
/*
 * "vol-pool" command
 */
static const vshCmdInfo info_vol_pool[] = {
    {"help", N_("returns the storage pool for a given volume key or path")},
    {"desc", ""},
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_pool[] = {
12381
    {"uuid", VSH_OT_BOOL, 0, N_("return the pool uuid rather than pool name")},
J
Justin Clift 已提交
12382 12383 12384 12385
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12386
static bool
J
Justin Clift 已提交
12387 12388 12389 12390
cmdVolPool(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
12391
    char uuid[VIR_UUID_STRING_BUFLEN];
J
Justin Clift 已提交
12392 12393

    /* Check the connection to libvirtd daemon is still working */
12394
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12395
        return false;
J
Justin Clift 已提交
12396 12397

    /* Use the supplied string to locate the volume */
12398
    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
J
Justin Clift 已提交
12399
                                   VSH_BYUUID))) {
E
Eric Blake 已提交
12400
        return false;
J
Justin Clift 已提交
12401 12402 12403 12404 12405 12406 12407
    }

    /* Look up the parent storage pool for the volume */
    pool = virStoragePoolLookupByVolume(vol);
    if (pool == NULL) {
        vshError(ctl, "%s", _("failed to get parent pool"));
        virStorageVolFree(vol);
E
Eric Blake 已提交
12408
        return false;
J
Justin Clift 已提交
12409 12410
    }

12411 12412 12413 12414 12415 12416 12417 12418 12419
    /* Return the requested details of the parent storage pool */
    if (vshCommandOptBool(cmd, "uuid")) {
        /* Retrieve and return pool UUID string */
        if (virStoragePoolGetUUIDString(pool, &uuid[0]) == 0)
            vshPrint(ctl, "%s\n", uuid);
    } else {
        /* Return the storage pool name */
        vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
    }
J
Justin Clift 已提交
12420 12421 12422 12423

    /* Cleanup */
    virStorageVolFree(vol);
    virStoragePoolFree(pool);
E
Eric Blake 已提交
12424
    return true;
J
Justin Clift 已提交
12425 12426
}

12427 12428 12429 12430

/*
 * "vol-key" command
 */
12431
static const vshCmdInfo info_vol_key[] = {
12432
    {"help", N_("returns the volume key for a given volume name or path")},
12433
    {"desc", ""},
12434 12435 12436
    {NULL, NULL}
};

12437
static const vshCmdOptDef opts_vol_key[] = {
12438
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or path")},
E
Eric Blake 已提交
12439
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
12440 12441 12442
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12443
static bool
12444
cmdVolKey(vshControl *ctl, const vshCmd *cmd)
12445 12446 12447
{
    virStorageVolPtr vol;

12448
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12449
        return false;
12450

12451
    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
12452
        return false;
12453 12454 12455

    vshPrint(ctl, "%s\n", virStorageVolGetKey(vol));
    virStorageVolFree(vol);
E
Eric Blake 已提交
12456
    return true;
12457 12458 12459 12460 12461 12462 12463
}



/*
 * "vol-path" command
 */
12464
static const vshCmdInfo info_vol_path[] = {
12465
    {"help", N_("returns the volume path for a given volume name or key")},
12466
    {"desc", ""},
12467 12468 12469
    {NULL, NULL}
};

12470
static const vshCmdOptDef opts_vol_path[] = {
12471
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or key")},
E
Eric Blake 已提交
12472
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
12473 12474 12475
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12476
static bool
12477
cmdVolPath(vshControl *ctl, const vshCmd *cmd)
12478 12479
{
    virStorageVolPtr vol;
12480
    char * StorageVolPath;
12481

12482
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12483
        return false;
12484

12485
    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) {
E
Eric Blake 已提交
12486
        return false;
12487
    }
12488

12489 12490 12491 12492 12493 12494 12495
    if ((StorageVolPath = virStorageVolGetPath(vol)) == NULL) {
        virStorageVolFree(vol);
        return false;
    }

    vshPrint(ctl, "%s\n", StorageVolPath);
    VIR_FREE(StorageVolPath);
12496
    virStorageVolFree(vol);
E
Eric Blake 已提交
12497
    return true;
12498 12499 12500
}


12501 12502 12503 12504
/*
 * "secret-define" command
 */
static const vshCmdInfo info_secret_define[] = {
12505 12506
    {"help", N_("define or modify a secret from an XML file")},
    {"desc", N_("Define or modify a secret.")},
12507 12508
    {NULL, NULL}
};
12509

12510
static const vshCmdOptDef opts_secret_define[] = {
12511
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
12512 12513
    {NULL, 0, 0, NULL}
};
12514

E
Eric Blake 已提交
12515
static bool
12516 12517
cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
{
12518
    const char *from = NULL;
12519
    char *buffer;
12520
    virSecretPtr res;
12521
    char uuid[VIR_UUID_STRING_BUFLEN];
12522

12523
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12524
        return false;
12525

12526
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
12527
        return false;
12528 12529

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
12530
        return false;
12531 12532

    res = virSecretDefineXML(ctl->conn, buffer, 0);
12533
    VIR_FREE(buffer);
12534 12535

    if (res == NULL) {
12536
        vshError(ctl, _("Failed to set attributes from %s"), from);
E
Eric Blake 已提交
12537
        return false;
12538
    }
12539
    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
12540
        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
12541
        virSecretFree(res);
E
Eric Blake 已提交
12542
        return false;
12543 12544 12545
    }
    vshPrint(ctl, _("Secret %s created\n"), uuid);
    virSecretFree(res);
E
Eric Blake 已提交
12546
    return true;
12547 12548 12549 12550 12551 12552
}

/*
 * "secret-dumpxml" command
 */
static const vshCmdInfo info_secret_dumpxml[] = {
12553 12554
    {"help", N_("secret attributes in XML")},
    {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
12555 12556 12557 12558
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_dumpxml[] = {
12559
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
12560 12561 12562
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12563
static bool
12564 12565 12566
cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
E
Eric Blake 已提交
12567
    bool ret = false;
12568 12569
    char *xml;

12570
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12571
        return false;
12572 12573 12574

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
12575
        return false;
12576 12577 12578 12579

    xml = virSecretGetXMLDesc(secret, 0);
    if (xml == NULL)
        goto cleanup;
12580
    vshPrint(ctl, "%s", xml);
12581
    VIR_FREE(xml);
E
Eric Blake 已提交
12582
    ret = true;
12583 12584 12585 12586 12587 12588 12589 12590 12591 12592

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-set-value" command
 */
static const vshCmdInfo info_secret_set_value[] = {
12593 12594
    {"help", N_("set a secret value")},
    {"desc", N_("Set a secret value.")},
12595 12596 12597 12598
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_set_value[] = {
12599 12600
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
    {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
12601 12602 12603
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12604
static bool
12605 12606 12607 12608
cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    size_t value_size;
12609
    const char *base64 = NULL;
12610
    char *value;
E
Eric Blake 已提交
12611 12612
    int res;
    bool ret = false;
12613

12614
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12615
        return false;
12616 12617 12618

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
12619
        return false;
12620

12621
    if (vshCommandOptString(cmd, "base64", &base64) <= 0)
12622 12623 12624
        goto cleanup;

    if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
J
Jim Meyering 已提交
12625
        vshError(ctl, "%s", _("Invalid base64 data"));
12626 12627 12628
        goto cleanup;
    }
    if (value == NULL) {
12629
        vshError(ctl, "%s", _("Failed to allocate memory"));
E
Eric Blake 已提交
12630
        return false;
12631 12632 12633 12634
    }

    res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
    memset(value, 0, value_size);
12635
    VIR_FREE(value);
12636 12637

    if (res != 0) {
12638
        vshError(ctl, "%s", _("Failed to set secret value"));
12639 12640 12641
        goto cleanup;
    }
    vshPrint(ctl, "%s", _("Secret value set\n"));
E
Eric Blake 已提交
12642
    ret = true;
12643 12644 12645 12646 12647 12648 12649 12650 12651 12652

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-get-value" command
 */
static const vshCmdInfo info_secret_get_value[] = {
12653 12654
    {"help", N_("Output a secret value")},
    {"desc", N_("Output a secret value to stdout.")},
12655 12656 12657 12658
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_get_value[] = {
12659
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
12660 12661 12662
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12663
static bool
12664 12665 12666 12667 12668 12669
cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    char *base64;
    unsigned char *value;
    size_t value_size;
E
Eric Blake 已提交
12670
    bool ret = false;
12671

12672
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12673
        return false;
12674 12675 12676

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
12677
        return false;
12678 12679 12680 12681 12682 12683 12684

    value = virSecretGetValue(secret, &value_size, 0);
    if (value == NULL)
        goto cleanup;

    base64_encode_alloc((char *)value, value_size, &base64);
    memset(value, 0, value_size);
12685
    VIR_FREE(value);
12686 12687

    if (base64 == NULL) {
12688
        vshError(ctl, "%s", _("Failed to allocate memory"));
12689 12690
        goto cleanup;
    }
12691
    vshPrint(ctl, "%s", base64);
12692
    memset(base64, 0, strlen(base64));
12693
    VIR_FREE(base64);
E
Eric Blake 已提交
12694
    ret = true;
12695 12696 12697 12698 12699 12700 12701 12702 12703 12704

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-undefine" command
 */
static const vshCmdInfo info_secret_undefine[] = {
12705 12706
    {"help", N_("undefine a secret")},
    {"desc", N_("Undefine a secret.")},
12707 12708 12709 12710
    {NULL, NULL}
};

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

E
Eric Blake 已提交
12715
static bool
12716 12717 12718
cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
E
Eric Blake 已提交
12719
    bool ret = false;
12720
    const char *uuid;
12721

12722
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12723
        return false;
12724 12725 12726

    secret = vshCommandOptSecret(ctl, cmd, &uuid);
    if (secret == NULL)
E
Eric Blake 已提交
12727
        return false;
12728 12729

    if (virSecretUndefine(secret) < 0) {
12730
        vshError(ctl, _("Failed to delete secret %s"), uuid);
12731 12732 12733
        goto cleanup;
    }
    vshPrint(ctl, _("Secret %s deleted\n"), uuid);
E
Eric Blake 已提交
12734
    ret = true;
12735 12736 12737 12738 12739 12740 12741 12742 12743 12744

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-list" command
 */
static const vshCmdInfo info_secret_list[] = {
12745 12746
    {"help", N_("list secrets")},
    {"desc", N_("Returns a list of secrets")},
12747 12748 12749
    {NULL, NULL}
};

E
Eric Blake 已提交
12750
static bool
12751 12752 12753 12754 12755
cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    int maxuuids = 0, i;
    char **uuids = NULL;

12756
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12757
        return false;
12758 12759 12760

    maxuuids = virConnectNumOfSecrets(ctl->conn);
    if (maxuuids < 0) {
12761
        vshError(ctl, "%s", _("Failed to list secrets"));
E
Eric Blake 已提交
12762
        return false;
12763 12764 12765 12766 12767
    }
    uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);

    maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
    if (maxuuids < 0) {
12768
        vshError(ctl, "%s", _("Failed to list secrets"));
12769
        VIR_FREE(uuids);
E
Eric Blake 已提交
12770
        return false;
12771 12772 12773 12774
    }

    qsort(uuids, maxuuids, sizeof(char *), namesorter);

12775 12776
    vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
    vshPrintExtra(ctl, "-----------------------------------------------------------\n");
12777 12778

    for (i = 0; i < maxuuids; i++) {
12779 12780 12781 12782
        virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]);
        const char *usageType = NULL;

        if (!sec) {
12783
            VIR_FREE(uuids[i]);
12784 12785 12786 12787 12788 12789 12790 12791 12792 12793 12794 12795 12796 12797 12798 12799 12800 12801
            continue;
        }

        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);
12802
        VIR_FREE(uuids[i]);
12803
    }
12804
    VIR_FREE(uuids);
E
Eric Blake 已提交
12805
    return true;
12806
}
12807 12808 12809 12810 12811


/*
 * "version" command
 */
12812
static const vshCmdInfo info_version[] = {
12813 12814
    {"help", N_("show version")},
    {"desc", N_("Display the system version information.")},
12815 12816 12817
    {NULL, NULL}
};

12818 12819 12820 12821
static const vshCmdOptDef opts_version[] = {
    {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
    {NULL, 0, 0, NULL}
};
12822

E
Eric Blake 已提交
12823
static bool
12824
cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
12825 12826 12827 12828 12829 12830
{
    unsigned long hvVersion;
    const char *hvType;
    unsigned long libVersion;
    unsigned long includeVersion;
    unsigned long apiVersion;
12831
    unsigned long daemonVersion;
12832 12833 12834 12835 12836
    int ret;
    unsigned int major;
    unsigned int minor;
    unsigned int rel;

12837
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12838
        return false;
12839 12840 12841

    hvType = virConnectGetType(ctl->conn);
    if (hvType == NULL) {
12842
        vshError(ctl, "%s", _("failed to get hypervisor type"));
E
Eric Blake 已提交
12843
        return false;
12844 12845 12846 12847 12848 12849 12850 12851 12852 12853 12854 12855
    }

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

    ret = virGetVersion(&libVersion, hvType, &apiVersion);
    if (ret < 0) {
12856
        vshError(ctl, "%s", _("failed to get the library version"));
E
Eric Blake 已提交
12857
        return false;
12858 12859 12860 12861 12862 12863 12864 12865 12866 12867 12868 12869 12870 12871 12872 12873 12874
    }
    major = libVersion / 1000000;
    libVersion %= 1000000;
    minor = libVersion / 1000;
    rel = libVersion % 1000;
    vshPrint(ctl, _("Using library: libvir %d.%d.%d\n"),
             major, minor, rel);

    major = apiVersion / 1000000;
    apiVersion %= 1000000;
    minor = apiVersion / 1000;
    rel = apiVersion % 1000;
    vshPrint(ctl, _("Using API: %s %d.%d.%d\n"), hvType,
             major, minor, rel);

    ret = virConnectGetVersion(ctl->conn, &hvVersion);
    if (ret < 0) {
12875
        vshError(ctl, "%s", _("failed to get the hypervisor version"));
E
Eric Blake 已提交
12876
        return false;
12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889
    }
    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;

        vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"),
                 hvType, major, minor, rel);
    }
12890 12891 12892 12893 12894 12895 12896 12897 12898 12899 12900 12901 12902 12903 12904

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

E
Eric Blake 已提交
12905
    return true;
12906 12907
}

12908 12909 12910 12911
/*
 * "nodedev-list" command
 */
static const vshCmdInfo info_node_list_devices[] = {
12912
    {"help", N_("enumerate devices on this host")},
12913
    {"desc", ""},
12914 12915 12916 12917
    {NULL, NULL}
};

static const vshCmdOptDef opts_node_list_devices[] = {
12918 12919
    {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
    {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
12920 12921 12922
    {NULL, 0, 0, NULL}
};

12923 12924 12925 12926 12927 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 12944
#define MAX_DEPTH 100
#define INDENT_SIZE 4
#define INDENT_BUFLEN ((MAX_DEPTH * INDENT_SIZE) + 1)

static void
cmdNodeListDevicesPrint(vshControl *ctl,
                        char **devices,
                        char **parents,
                        int num_devices,
                        int devid,
                        int lastdev,
                        unsigned int depth,
                        unsigned int indentIdx,
                        char *indentBuf)
{
    int i;
    int nextlastdev = -1;

    /* Prepare indent for this device, but not if at root */
    if (depth && depth < MAX_DEPTH) {
        indentBuf[indentIdx] = '+';
        indentBuf[indentIdx+1] = '-';
12945 12946
        indentBuf[indentIdx+2] = ' ';
        indentBuf[indentIdx+3] = '\0';
12947 12948 12949
    }

    /* Print this device */
12950
    vshPrint(ctl, "%s", indentBuf);
12951 12952 12953 12954 12955 12956 12957 12958 12959 12960 12961 12962 12963 12964 12965 12966 12967 12968 12969 12970 12971 12972 12973
    vshPrint(ctl, "%s\n", devices[devid]);


    /* Update indent to show '|' or ' ' for child devices */
    if (depth && depth < MAX_DEPTH) {
        if (devid == lastdev)
            indentBuf[indentIdx] = ' ';
        else
            indentBuf[indentIdx] = '|';
        indentBuf[indentIdx+1] = ' ';
        indentIdx+=2;
    }

    /* Determine the index of the last child device */
    for (i = 0 ; i < num_devices ; i++) {
        if (parents[i] &&
            STREQ(parents[i], devices[devid])) {
            nextlastdev = i;
        }
    }

    /* If there is a child device, then print another blank line */
    if (nextlastdev != -1) {
12974
        vshPrint(ctl, "%s", indentBuf);
12975
        vshPrint(ctl, " |\n");
12976 12977 12978 12979 12980 12981 12982 12983 12984 12985 12986 12987 12988 12989 12990 12991 12992 12993 12994 12995 12996 12997
    }

    /* Finally print all children */
    if (depth < MAX_DEPTH)
        indentBuf[indentIdx] = ' ';
    for (i = 0 ; i < num_devices ; i++) {
        if (depth < MAX_DEPTH) {
            indentBuf[indentIdx] = ' ';
            indentBuf[indentIdx+1] = ' ';
        }
        if (parents[i] &&
            STREQ(parents[i], devices[devid]))
            cmdNodeListDevicesPrint(ctl, devices, parents,
                                    num_devices, i, nextlastdev,
                                    depth + 1, indentIdx + 2, indentBuf);
        if (depth < MAX_DEPTH)
            indentBuf[indentIdx] = '\0';
    }

    /* 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) {
12998
        vshPrint(ctl, "%s", indentBuf);
12999 13000 13001 13002
        vshPrint(ctl, "\n");
    }
}

E
Eric Blake 已提交
13003
static bool
13004 13005
cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
13006
    const char *cap = NULL;
13007
    char **devices;
13008
    int num_devices, i;
13009
    int tree = vshCommandOptBool(cmd, "tree");
13010

13011
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13012
        return false;
13013

13014
    if (vshCommandOptString(cmd, "cap", &cap) <= 0)
13015 13016 13017 13018
        cap = NULL;

    num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
    if (num_devices < 0) {
13019
        vshError(ctl, "%s", _("Failed to count node devices"));
E
Eric Blake 已提交
13020
        return false;
13021
    } else if (num_devices == 0) {
E
Eric Blake 已提交
13022
        return true;
13023 13024 13025 13026 13027 13028
    }

    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
    num_devices =
        virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
    if (num_devices < 0) {
13029
        vshError(ctl, "%s", _("Failed to list node devices"));
13030
        VIR_FREE(devices);
E
Eric Blake 已提交
13031
        return false;
13032
    }
13033
    qsort(&devices[0], num_devices, sizeof(char*), namesorter);
13034 13035 13036 13037 13038 13039 13040
    if (tree) {
        char indentBuf[INDENT_BUFLEN];
        char **parents = vshMalloc(ctl, sizeof(char *) * num_devices);
        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);
E
Eric Blake 已提交
13041
                parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
13042 13043 13044 13045 13046 13047 13048 13049 13050 13051 13052 13053 13054 13055 13056 13057 13058 13059 13060
            } else {
                parents[i] = NULL;
            }
            virNodeDeviceFree(dev);
        }
        for (i = 0 ; i < num_devices ; i++) {
            memset(indentBuf, '\0', sizeof indentBuf);
            if (parents[i] == NULL)
                cmdNodeListDevicesPrint(ctl,
                                        devices,
                                        parents,
                                        num_devices,
                                        i,
                                        i,
                                        0,
                                        0,
                                        indentBuf);
        }
        for (i = 0 ; i < num_devices ; i++) {
13061 13062
            VIR_FREE(devices[i]);
            VIR_FREE(parents[i]);
13063
        }
13064
        VIR_FREE(parents);
13065 13066 13067
    } else {
        for (i = 0; i < num_devices; i++) {
            vshPrint(ctl, "%s\n", devices[i]);
13068
            VIR_FREE(devices[i]);
13069
        }
13070
    }
13071
    VIR_FREE(devices);
E
Eric Blake 已提交
13072
    return true;
13073 13074 13075 13076 13077 13078
}

/*
 * "nodedev-dumpxml" command
 */
static const vshCmdInfo info_node_device_dumpxml[] = {
13079 13080
    {"help", N_("node device details in XML")},
    {"desc", N_("Output the node device details as an XML dump to stdout.")},
13081 13082 13083 13084 13085
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_dumpxml[] = {
13086
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
13087 13088 13089
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13090
static bool
13091 13092
cmdNodeDeviceDumpXML (vshControl *ctl, const vshCmd *cmd)
{
13093
    const char *name = NULL;
13094
    virNodeDevicePtr device;
L
Laine Stump 已提交
13095
    char *xml;
13096

13097
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13098
        return false;
13099
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
13100
        return false;
13101
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
13102
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
13103
        return false;
13104 13105
    }

L
Laine Stump 已提交
13106 13107 13108
    xml = virNodeDeviceGetXMLDesc(device, 0);
    if (!xml) {
        virNodeDeviceFree(device);
E
Eric Blake 已提交
13109
        return false;
L
Laine Stump 已提交
13110 13111 13112
    }

    vshPrint(ctl, "%s\n", xml);
13113
    VIR_FREE(xml);
13114
    virNodeDeviceFree(device);
E
Eric Blake 已提交
13115
    return true;
13116 13117
}

13118
/*
13119
 * "nodedev-detach" command
13120
 */
13121 13122 13123
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.")},
13124 13125 13126 13127
    {NULL, NULL}
};


13128
static const vshCmdOptDef opts_node_device_detach[] = {
13129
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
13130 13131 13132
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13133
static bool
13134
cmdNodeDeviceDetach (vshControl *ctl, const vshCmd *cmd)
13135
{
13136
    const char *name = NULL;
13137
    virNodeDevicePtr device;
E
Eric Blake 已提交
13138
    bool ret = true;
13139

13140
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13141
        return false;
13142
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
13143
        return false;
13144
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
13145
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
13146
        return false;
13147 13148
    }

13149 13150
    /* Yes, our public API is misspelled.  At least virsh can accept
     * either spelling.  */
13151
    if (virNodeDeviceDettach(device) == 0) {
13152
        vshPrint(ctl, _("Device %s detached\n"), name);
13153
    } else {
13154
        vshError(ctl, _("Failed to detach device %s"), name);
E
Eric Blake 已提交
13155
        ret = false;
13156 13157 13158 13159 13160 13161 13162 13163 13164
    }
    virNodeDeviceFree(device);
    return ret;
}

/*
 * "nodedev-reattach" command
 */
static const vshCmdInfo info_node_device_reattach[] = {
13165 13166
    {"help", N_("reattach node device to its device driver")},
    {"desc", N_("Reattach node device to its device driver once released by the domain.")},
13167 13168 13169 13170 13171
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_reattach[] = {
13172
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
13173 13174 13175
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13176
static bool
13177 13178
cmdNodeDeviceReAttach (vshControl *ctl, const vshCmd *cmd)
{
13179
    const char *name = NULL;
13180
    virNodeDevicePtr device;
E
Eric Blake 已提交
13181
    bool ret = true;
13182

13183
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13184
        return false;
13185
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
13186
        return false;
13187
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
13188
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
13189
        return false;
13190 13191 13192 13193 13194
    }

    if (virNodeDeviceReAttach(device) == 0) {
        vshPrint(ctl, _("Device %s re-attached\n"), name);
    } else {
13195
        vshError(ctl, _("Failed to re-attach device %s"), name);
E
Eric Blake 已提交
13196
        ret = false;
13197 13198 13199 13200 13201 13202 13203 13204 13205
    }
    virNodeDeviceFree(device);
    return ret;
}

/*
 * "nodedev-reset" command
 */
static const vshCmdInfo info_node_device_reset[] = {
13206 13207
    {"help", N_("reset node device")},
    {"desc", N_("Reset node device before or after assigning to a domain.")},
13208 13209 13210 13211 13212
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_reset[] = {
13213
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
13214 13215 13216
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13217
static bool
13218 13219
cmdNodeDeviceReset (vshControl *ctl, const vshCmd *cmd)
{
13220
    const char *name = NULL;
13221
    virNodeDevicePtr device;
E
Eric Blake 已提交
13222
    bool ret = true;
13223

13224
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13225
        return false;
13226
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
13227
        return false;
13228
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
13229
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
13230
        return false;
13231 13232 13233 13234 13235
    }

    if (virNodeDeviceReset(device) == 0) {
        vshPrint(ctl, _("Device %s reset\n"), name);
    } else {
13236
        vshError(ctl, _("Failed to reset device %s"), name);
E
Eric Blake 已提交
13237
        ret = false;
13238 13239 13240 13241 13242
    }
    virNodeDeviceFree(device);
    return ret;
}

13243
/*
E
Eric Blake 已提交
13244
 * "hostname" command
13245
 */
13246
static const vshCmdInfo info_hostname[] = {
13247
    {"help", N_("print the hypervisor hostname")},
13248
    {"desc", ""},
13249 13250 13251
    {NULL, NULL}
};

E
Eric Blake 已提交
13252
static bool
13253
cmdHostname (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
13254 13255 13256
{
    char *hostname;

13257
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13258
        return false;
13259 13260 13261

    hostname = virConnectGetHostname (ctl->conn);
    if (hostname == NULL) {
13262
        vshError(ctl, "%s", _("failed to get hostname"));
E
Eric Blake 已提交
13263
        return false;
13264 13265 13266
    }

    vshPrint (ctl, "%s\n", hostname);
13267
    VIR_FREE(hostname);
13268

E
Eric Blake 已提交
13269
    return true;
13270 13271 13272 13273 13274
}

/*
 * "uri" command
 */
13275
static const vshCmdInfo info_uri[] = {
13276
    {"help", N_("print the hypervisor canonical URI")},
13277
    {"desc", ""},
13278 13279 13280
    {NULL, NULL}
};

E
Eric Blake 已提交
13281
static bool
13282
cmdURI (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
13283 13284 13285
{
    char *uri;

13286
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13287
        return false;
13288 13289 13290

    uri = virConnectGetURI (ctl->conn);
    if (uri == NULL) {
13291
        vshError(ctl, "%s", _("failed to get URI"));
E
Eric Blake 已提交
13292
        return false;
13293 13294 13295
    }

    vshPrint (ctl, "%s\n", uri);
13296
    VIR_FREE(uri);
13297

E
Eric Blake 已提交
13298
    return true;
13299 13300
}

E
Eric Blake 已提交
13301 13302 13303 13304 13305 13306 13307 13308 13309 13310
/*
 * "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}
};

E
Eric Blake 已提交
13311
static bool
E
Eric Blake 已提交
13312 13313 13314 13315 13316
cmdSysinfo (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *sysinfo;

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13317
        return false;
E
Eric Blake 已提交
13318 13319 13320 13321

    sysinfo = virConnectGetSysinfo (ctl->conn, 0);
    if (sysinfo == NULL) {
        vshError(ctl, "%s", _("failed to get sysinfo"));
E
Eric Blake 已提交
13322
        return false;
E
Eric Blake 已提交
13323 13324 13325 13326 13327
    }

    vshPrint (ctl, "%s", sysinfo);
    VIR_FREE(sysinfo);

E
Eric Blake 已提交
13328
    return true;
E
Eric Blake 已提交
13329 13330
}

13331 13332 13333
/*
 * "vncdisplay" command
 */
13334
static const vshCmdInfo info_vncdisplay[] = {
13335 13336
    {"help", N_("vnc display")},
    {"desc", N_("Output the IP address and port number for the VNC display.")},
13337 13338 13339
    {NULL, NULL}
};

13340
static const vshCmdOptDef opts_vncdisplay[] = {
13341
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
13342 13343 13344
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13345
static bool
13346
cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
13347 13348 13349 13350 13351
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virDomainPtr dom;
E
Eric Blake 已提交
13352
    bool ret = false;
13353 13354 13355
    int port = 0;
    char *doc;

13356
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13357
        return false;
13358

J
Jim Meyering 已提交
13359
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
13360
        return false;
13361 13362 13363 13364 13365

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

13366
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
13367
    VIR_FREE(doc);
13368 13369 13370 13371 13372 13373 13374 13375 13376 13377 13378 13379 13380 13381 13382
    if (!xml)
        goto cleanup;

    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/graphics[@type='vnc']/@port)", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
        goto cleanup;
    }
    if (virStrToLong_i((const char *)obj->stringval, NULL, 10, &port) || port < 0)
        goto cleanup;
    xmlXPathFreeObject(obj);

    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/graphics[@type='vnc']/@listen)", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0) ||
13383
        STREQ((const char*)obj->stringval, "0.0.0.0")) {
13384 13385 13386 13387 13388 13389
        vshPrint(ctl, ":%d\n", port-5900);
    } else {
        vshPrint(ctl, "%s:%d\n", (const char *)obj->stringval, port-5900);
    }
    xmlXPathFreeObject(obj);
    obj = NULL;
E
Eric Blake 已提交
13390
    ret = true;
13391 13392 13393 13394

 cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
13395
    xmlFreeDoc(xml);
13396 13397 13398 13399 13400 13401 13402
    virDomainFree(dom);
    return ret;
}

/*
 * "ttyconsole" command
 */
13403
static const vshCmdInfo info_ttyconsole[] = {
13404 13405
    {"help", N_("tty console")},
    {"desc", N_("Output the device for the TTY console.")},
13406 13407 13408
    {NULL, NULL}
};

13409
static const vshCmdOptDef opts_ttyconsole[] = {
13410
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
13411 13412 13413
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13414
static bool
13415
cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
13416 13417 13418 13419 13420
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virDomainPtr dom;
E
Eric Blake 已提交
13421
    bool ret = false;
13422 13423
    char *doc;

13424
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13425
        return false;
13426

J
Jim Meyering 已提交
13427
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
13428
        return false;
13429 13430 13431 13432 13433

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

13434
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
13435
    VIR_FREE(doc);
13436 13437 13438 13439 13440 13441 13442 13443 13444
    if (!xml)
        goto cleanup;

    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
        goto cleanup;
    }
    vshPrint(ctl, "%s\n", (const char *)obj->stringval);
E
Eric Blake 已提交
13445
    ret = true;
13446 13447 13448 13449

 cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
13450
    xmlFreeDoc(xml);
13451 13452 13453 13454 13455 13456
    virDomainFree(dom);
    return ret;
}

/*
 * "attach-device" command
13457
 */
13458
static const vshCmdInfo info_attach_device[] = {
13459 13460
    {"help", N_("attach device from an XML file")},
    {"desc", N_("Attach device from an XML <file>.")},
13461 13462 13463
    {NULL, NULL}
};

13464
static const vshCmdOptDef opts_attach_device[] = {
13465 13466
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
13467 13468
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
13469 13470 13471
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13472
static bool
13473
cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
13474 13475
{
    virDomainPtr dom;
13476
    const char *from = NULL;
13477
    char *buffer;
W
Wen Congyang 已提交
13478
    int ret;
J
Jim Fehlig 已提交
13479
    unsigned int flags;
13480

13481
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13482
        return false;
13483

J
Jim Meyering 已提交
13484
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
13485
        return false;
13486

13487
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
13488
        virDomainFree(dom);
E
Eric Blake 已提交
13489
        return false;
13490 13491
    }

13492
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
13493
        virshReportError(ctl);
13494
        virDomainFree(dom);
E
Eric Blake 已提交
13495
        return false;
13496
    }
13497

13498
    if (vshCommandOptBool(cmd, "config")) {
13499
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
13500
        if (virDomainIsActive(dom) == 1)
13501
           flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
13502 13503 13504 13505
        ret = virDomainAttachDeviceFlags(dom, buffer, flags);
    } else {
        ret = virDomainAttachDevice(dom, buffer);
    }
13506
    VIR_FREE(buffer);
13507 13508

    if (ret < 0) {
13509
        vshError(ctl, _("Failed to attach device from %s"), from);
13510
        virDomainFree(dom);
E
Eric Blake 已提交
13511
        return false;
13512
    } else {
J
Jim Meyering 已提交
13513
        vshPrint(ctl, "%s", _("Device attached successfully\n"));
13514 13515 13516
    }

    virDomainFree(dom);
E
Eric Blake 已提交
13517
    return true;
13518 13519
}

13520 13521 13522 13523 13524 13525 13526
/**
 * Check if n1 is superset of n2, meaning n1 contains all elements and
 * attributes as n2 at least. Including children.
 * @n1 first node
 * @n2 second node
 * returns true in case n1 covers n2, false otherwise.
 */
13527
ATTRIBUTE_UNUSED
13528 13529 13530 13531 13532 13533 13534 13535 13536
static bool
vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2)
{
    xmlNodePtr child1, child2;
    xmlAttrPtr attr;
    char *prop1, *prop2;
    bool found;
    bool visited;
    bool ret = false;
E
Eric Blake 已提交
13537
    long n1_child_size, n2_child_size, n1_iter;
13538 13539 13540 13541 13542 13543 13544 13545 13546 13547 13548 13549 13550 13551 13552 13553 13554 13555 13556 13557 13558 13559 13560 13561 13562 13563 13564 13565
    virBitmapPtr bitmap;

    if (!n1 && !n2)
        return true;

    if (!n1 || !n2)
        return false;

    if (!xmlStrEqual(n1->name, n2->name))
        return false;

    /* Iterate over n2 attributes and check if n1 contains them*/
    attr = n2->properties;
    while (attr) {
        if (attr->type == XML_ATTRIBUTE_NODE) {
            prop1 = virXMLPropString(n1, (const char *) attr->name);
            prop2 = virXMLPropString(n2, (const char *) attr->name);
            if (STRNEQ_NULLABLE(prop1, prop2)) {
                xmlFree(prop1);
                xmlFree(prop2);
                return false;
            }
            xmlFree(prop1);
            xmlFree(prop2);
        }
        attr = attr->next;
    }

E
Eric Blake 已提交
13566 13567 13568 13569
    n1_child_size = virXMLChildElementCount(n1);
    n2_child_size = virXMLChildElementCount(n2);
    if (n1_child_size < 0 || n2_child_size < 0 ||
        n1_child_size < n2_child_size)
13570 13571
        return false;

13572 13573 13574
    if (n1_child_size == 0 && n2_child_size == 0)
        return true;

13575 13576 13577 13578 13579 13580 13581 13582 13583 13584 13585 13586 13587 13588 13589 13590 13591 13592 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 13603 13604 13605 13606 13607 13608 13609 13610 13611 13612 13613 13614 13615 13616 13617 13618 13619 13620 13621 13622 13623 13624 13625 13626 13627 13628 13629 13630 13631 13632 13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 13643 13644 13645 13646 13647 13648 13649 13650 13651 13652
    if (!(bitmap = virBitmapAlloc(n1_child_size))) {
        virReportOOMError();
        return false;
    }

    child2 = n2->children;
    while (child2) {
        if (child2->type != XML_ELEMENT_NODE) {
            child2 = child2->next;
            continue;
        }

        child1 = n1->children;
        n1_iter = 0;
        found = false;
        while (child1) {
            if (child1->type != XML_ELEMENT_NODE) {
                child1 = child1->next;
                continue;
            }

            if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
                vshError(NULL, "%s", _("Bad child elements counting."));
                goto cleanup;
            }

            if (visited) {
                child1 = child1->next;
                n1_iter++;
                continue;
            }

            if (xmlStrEqual(child1->name, child2->name)) {
                found = true;
                if (virBitmapSetBit(bitmap, n1_iter) < 0) {
                    vshError(NULL, "%s", _("Bad child elements counting."));
                    goto cleanup;
                }

                if (!vshNodeIsSuperset(child1, child2))
                    goto cleanup;

                break;
            }

            child1 = child1->next;
            n1_iter++;
        }

        if (!found)
            goto cleanup;

        child2 = child2->next;
    }

    ret = true;

cleanup:
    virBitmapFree(bitmap);
    return ret;
}

/**
 * vshCompleteXMLFromDomain:
 * @ctl vshControl for error messages printing
 * @dom domain
 * @oldXML device XML before
 * @newXML and after completion
 *
 * For given domain and (probably incomplete) device XML specification try to
 * find such device in domain and complete missing parts. This is however
 * possible only when given device XML is sufficiently precise so it addresses
 * only one device.
 *
 * Returns -2 when no such device exists in domain, -3 when given XML selects many
 *          (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
 *          is touched only in case of success.
 */
13653
ATTRIBUTE_UNUSED
13654 13655 13656 13657 13658 13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 13673 13674 13675 13676 13677 13678 13679 13680 13681 13682 13683 13684 13685 13686 13687 13688 13689 13690 13691 13692 13693 13694 13695 13696 13697 13698 13699 13700 13701 13702 13703 13704 13705 13706 13707 13708 13709 13710 13711 13712 13713 13714 13715 13716 13717 13718 13719 13720 13721 13722 13723 13724 13725 13726 13727 13728 13729 13730 13731 13732 13733 13734 13735 13736 13737 13738 13739 13740 13741 13742 13743 13744 13745 13746 13747 13748 13749 13750 13751 13752 13753 13754 13755 13756 13757 13758 13759 13760 13761 13762 13763 13764 13765
static int
vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
                         char **newXML)
{
    int funcRet = -1;
    char *domXML = NULL;
    xmlDocPtr domDoc = NULL, devDoc = NULL;
    xmlNodePtr node = NULL;
    xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
    xmlNodePtr *devices = NULL;
    xmlSaveCtxtPtr sctxt = NULL;
    int devices_size;
    char *xpath = NULL;
    xmlBufferPtr buf = NULL;
    int i = 0;
    int indx = -1;

    if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
        vshError(ctl, _("couldn't get XML description of domain %s"),
                 virDomainGetName(dom));
        goto cleanup;
    }

    domDoc = virXMLParseStringCtxt(domXML, _("(domain_definition)"), &domCtxt);
    if (!domDoc) {
        vshError(ctl, _("Failed to parse domain definition xml"));
        goto cleanup;
    }

    devDoc = virXMLParseStringCtxt(oldXML, _("(device_definition)"), &devCtxt);
    if (!devDoc) {
        vshError(ctl, _("Failed to parse device definition xml"));
        goto cleanup;
    }

    node = xmlDocGetRootElement(devDoc);

    buf = xmlBufferCreate();
    if (!buf) {
        vshError(ctl, "%s", _("out of memory"));
        goto cleanup;
    }

    /* Get all possible devices */
    virAsprintf(&xpath, "/domain/devices/%s", node->name);
    if (!xpath) {
        virReportOOMError();
        goto cleanup;
    }
    devices_size = virXPathNodeSet(xpath, domCtxt, &devices);

    if (devices_size < 0) {
        /* error */
        vshError(ctl, "%s", _("error when selecting nodes"));
        goto cleanup;
    } else if (devices_size == 0) {
        /* no such device */
        funcRet = -2;
        goto cleanup;
    }

    /* and refine */
    for (i = 0; i < devices_size; i++) {
        if (vshNodeIsSuperset(devices[i], node)) {
            if (indx >= 0) {
                funcRet = -3; /* ambiguous */
                goto cleanup;
            }
            indx = i;
        }
    }

    if (indx < 0) {
        funcRet = -2; /* no such device */
        goto cleanup;
    }

    vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %d\n", indx);

    if (newXML) {
        sctxt = xmlSaveToBuffer(buf, NULL, 0);
        if (!sctxt) {
            vshError(ctl, "%s", _("failed to create document saving context"));
            goto cleanup;
        }

        xmlSaveTree(sctxt, devices[indx]);
        xmlSaveClose(sctxt);
        *newXML = (char *) xmlBufferContent(buf);
        if (!*newXML) {
            virReportOOMError();
            goto cleanup;
        }
        buf->content = NULL;
    }

    vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML,
             newXML ? NULLSTR(*newXML) : "(null)");

    funcRet = 0;

cleanup:
    xmlBufferFree(buf);
    VIR_FREE(devices);
    xmlXPathFreeContext(devCtxt);
    xmlXPathFreeContext(domCtxt);
    xmlFreeDoc(devDoc);
    xmlFreeDoc(domDoc);
    VIR_FREE(domXML);
    VIR_FREE(xpath);
    return funcRet;
}
13766 13767 13768 13769

/*
 * "detach-device" command
 */
13770
static const vshCmdInfo info_detach_device[] = {
13771 13772
    {"help", N_("detach device from an XML file")},
    {"desc", N_("Detach device from an XML <file>")},
13773 13774 13775
    {NULL, NULL}
};

13776
static const vshCmdOptDef opts_detach_device[] = {
13777 13778
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
13779 13780
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
13781 13782 13783
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13784
static bool
13785
cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
13786
{
13787
    virDomainPtr dom = NULL;
13788
    const char *from = NULL;
13789
    char *buffer = NULL;
W
Wen Congyang 已提交
13790
    int ret;
13791
    bool funcRet = false;
J
Jim Fehlig 已提交
13792
    unsigned int flags;
13793

13794
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13795
        return false;
13796

J
Jim Meyering 已提交
13797
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
13798
        return false;
13799

13800 13801
    if (vshCommandOptString(cmd, "file", &from) <= 0)
        goto cleanup;
13802

13803
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
13804
        virshReportError(ctl);
13805 13806 13807
        goto cleanup;
    }

13808
    if (vshCommandOptBool(cmd, "config")) {
13809
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
13810
        if (virDomainIsActive(dom) == 1)
13811
           flags |= VIR_DOMAIN_AFFECT_LIVE;
13812
        ret = virDomainDetachDeviceFlags(dom, buffer, flags);
J
Jim Fehlig 已提交
13813
    } else {
13814
        ret = virDomainDetachDevice(dom, buffer);
J
Jim Fehlig 已提交
13815
    }
13816 13817

    if (ret < 0) {
13818
        vshError(ctl, _("Failed to detach device from %s"), from);
13819
        goto cleanup;
13820 13821
    }

13822 13823 13824 13825 13826
    vshPrint(ctl, "%s", _("Device detached successfully\n"));
    funcRet = true;

cleanup:
    VIR_FREE(buffer);
13827
    virDomainFree(dom);
13828
    return funcRet;
13829 13830
}

13831

13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842 13843
/*
 * "update-device" command
 */
static const vshCmdInfo info_update_device[] = {
    {"help", N_("update device from an XML file")},
    {"desc", N_("Update device from an XML <file>.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_update_device[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
13844 13845
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
13846
    {"force",  VSH_OT_BOOL, 0, N_("force device update")},
13847 13848 13849
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13850
static bool
13851 13852 13853
cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
13854
    const char *from = NULL;
13855
    char *buffer;
W
Wen Congyang 已提交
13856
    int ret;
13857 13858
    unsigned int flags;

13859
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13860
        return false;
13861 13862

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
13863
        return false;
13864

13865
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
13866
        virDomainFree(dom);
E
Eric Blake 已提交
13867
        return false;
13868 13869 13870
    }

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
13871
        virshReportError(ctl);
13872
        virDomainFree(dom);
E
Eric Blake 已提交
13873
        return false;
13874 13875
    }

13876
    if (vshCommandOptBool(cmd, "config")) {
13877
        flags = VIR_DOMAIN_AFFECT_CONFIG;
13878
        if (virDomainIsActive(dom) == 1)
13879
           flags |= VIR_DOMAIN_AFFECT_LIVE;
13880
    } else {
13881
        flags = VIR_DOMAIN_AFFECT_LIVE;
13882
    }
13883 13884 13885 13886

    if (vshCommandOptBool(cmd, "force"))
        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;

13887 13888 13889 13890 13891 13892
    ret = virDomainUpdateDeviceFlags(dom, buffer, flags);
    VIR_FREE(buffer);

    if (ret < 0) {
        vshError(ctl, _("Failed to update device from %s"), from);
        virDomainFree(dom);
E
Eric Blake 已提交
13893
        return false;
13894 13895 13896 13897 13898
    } else {
        vshPrint(ctl, "%s", _("Device updated successfully\n"));
    }

    virDomainFree(dom);
E
Eric Blake 已提交
13899
    return true;
13900 13901 13902
}


13903 13904 13905
/*
 * "attach-interface" command
 */
13906
static const vshCmdInfo info_attach_interface[] = {
13907 13908
    {"help", N_("attach network interface")},
    {"desc", N_("Attach new network interface.")},
13909 13910 13911
    {NULL, NULL}
};

13912
static const vshCmdOptDef opts_attach_interface[] = {
13913 13914 13915 13916 13917 13918
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"type",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")},
    {"source", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source of network interface")},
    {"target", VSH_OT_DATA, 0, N_("target network name")},
    {"mac",    VSH_OT_DATA, 0, N_("MAC address")},
    {"script", VSH_OT_DATA, 0, N_("script used to bridge network interface")},
13919
    {"model", VSH_OT_DATA, 0, N_("model type")},
13920 13921
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
13922 13923
    {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")},
    {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")},
13924 13925 13926
    {NULL, 0, 0, NULL}
};

13927 13928 13929
/* parse inbound and outbound which are in the format of
 * 'average,peak,burst', in which peak and burst are optional,
 * thus 'average,,burst' and 'average,peak' are also legal. */
13930
static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate)
13931 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 13943 13944 13945 13946 13947 13948 13949 13950 13951 13952 13953 13954 13955 13956 13957 13958 13959 13960 13961
{
    const char *average = NULL;
    char *peak = NULL, *burst = NULL;

    average = rateStr;
    if (!average)
        return -1;
    if (virStrToLong_ull(average, &peak, 10, &rate->average) < 0)
        return -1;

    /* peak will be updated to point to the end of rateStr in case
     * of 'average' */
    if (peak && *peak != '\0') {
        burst = strchr(peak + 1, ',');
        if (!(burst && (burst - peak == 1))) {
            if (virStrToLong_ull(peak + 1, &burst, 10, &rate->peak) < 0)
                return -1;
        }

        /* burst will be updated to point to the end of rateStr in case
         * of 'average,peak' */
        if (burst && *burst != '\0') {
            if (virStrToLong_ull(burst + 1, NULL, 10, &rate->burst) < 0)
                return -1;
        }
    }


    return 0;
}

E
Eric Blake 已提交
13962
static bool
13963
cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
13964 13965
{
    virDomainPtr dom = NULL;
13966
    const char *mac = NULL, *target = NULL, *script = NULL,
13967 13968
                *type = NULL, *source = NULL, *model = NULL,
                *inboundStr = NULL, *outboundStr = NULL;
13969
    virNetDevBandwidthRate inbound, outbound;
E
Eric Blake 已提交
13970
    int typ;
W
Wen Congyang 已提交
13971 13972
    int ret;
    bool functionReturn = false;
J
Jim Fehlig 已提交
13973
    unsigned int flags;
13974 13975
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *xml;
13976

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

J
Jim Meyering 已提交
13980
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
13981 13982
        goto cleanup;

13983
    if (vshCommandOptString(cmd, "type", &type) <= 0)
13984 13985
        goto cleanup;

13986 13987 13988 13989
    if (vshCommandOptString(cmd, "source", &source) < 0 ||
        vshCommandOptString(cmd, "target", &target) < 0 ||
        vshCommandOptString(cmd, "mac", &mac) < 0 ||
        vshCommandOptString(cmd, "script", &script) < 0 ||
13990 13991 13992
        vshCommandOptString(cmd, "model", &model) < 0 ||
        vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
13993
        vshError(ctl, "missing argument");
13994
        goto cleanup;
13995
    }
13996 13997

    /* check interface type */
13998
    if (STREQ(type, "network")) {
13999
        typ = 1;
14000
    } else if (STREQ(type, "bridge")) {
14001 14002
        typ = 2;
    } else {
E
Eric Blake 已提交
14003 14004
        vshError(ctl, _("No support for %s in command 'attach-interface'"),
                 type);
14005 14006 14007
        goto cleanup;
    }

14008 14009 14010 14011 14012 14013 14014 14015 14016 14017 14018 14019 14020 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030
    if (inboundStr) {
        memset(&inbound, 0, sizeof(inbound));
        if (parseRateStr(inboundStr, &inbound) < 0) {
            vshError(ctl, _("inbound format is incorrect"));
            goto cleanup;
        }
        if (inbound.average == 0) {
            vshError(ctl, _("inbound average is mandatory"));
            goto cleanup;
        }
    }
    if (outboundStr) {
        memset(&outbound, 0, sizeof(outbound));
        if (parseRateStr(outboundStr, &outbound) < 0) {
            vshError(ctl, _("outbound format is incorrect"));
            goto cleanup;
        }
        if (outbound.average == 0) {
            vshError(ctl, _("outbound average is mandatory"));
            goto cleanup;
        }
    }

14031
    /* Make XML of interface */
14032
    virBufferAsprintf(&buf, "<interface type='%s'>\n", type);
14033

14034
    if (typ == 1)
14035
        virBufferAsprintf(&buf, "  <source network='%s'/>\n", source);
14036
    else if (typ == 2)
14037
        virBufferAsprintf(&buf, "  <source bridge='%s'/>\n", source);
14038

14039
    if (target != NULL)
14040
        virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
14041
    if (mac != NULL)
14042
        virBufferAsprintf(&buf, "  <mac address='%s'/>\n", mac);
14043
    if (script != NULL)
14044
        virBufferAsprintf(&buf, "  <script path='%s'/>\n", script);
14045
    if (model != NULL)
14046
        virBufferAsprintf(&buf, "  <model type='%s'/>\n", model);
14047

14048 14049 14050 14051 14052 14053 14054 14055 14056 14057 14058 14059 14060 14061 14062 14063 14064 14065 14066 14067 14068
    if (inboundStr || outboundStr) {
        virBufferAsprintf(&buf, "  <bandwidth>\n");
        if (inboundStr && inbound.average > 0) {
            virBufferAsprintf(&buf, "    <inbound average='%llu'", inbound.average);
            if (inbound.peak > 0)
                virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
            if (inbound.burst > 0)
                virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
            virBufferAsprintf(&buf, "/>\n");
        }
        if (outboundStr && outbound.average > 0) {
            virBufferAsprintf(&buf, "    <outbound average='%llu'", outbound.average);
            if (outbound.peak > 0)
                virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
            if (outbound.burst > 0)
                virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
            virBufferAsprintf(&buf, "/>\n");
        }
        virBufferAsprintf(&buf, "  </bandwidth>\n");
    }

14069
    virBufferAddLit(&buf, "</interface>\n");
14070

14071 14072
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
W
Wen Congyang 已提交
14073
        goto cleanup;
14074 14075
    }

14076
    xml = virBufferContentAndReset(&buf);
14077

14078
    if (vshCommandOptBool(cmd, "config")) {
14079
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
14080
        if (virDomainIsActive(dom) == 1)
14081
            flags |= VIR_DOMAIN_AFFECT_LIVE;
14082
        ret = virDomainAttachDeviceFlags(dom, xml, flags);
14083
    } else {
14084
        ret = virDomainAttachDevice(dom, xml);
14085
    }
14086

14087 14088
    VIR_FREE(xml);

J
Jim Fehlig 已提交
14089
    if (ret != 0) {
L
Laine Stump 已提交
14090
        vshError(ctl, "%s", _("Failed to attach interface"));
J
Jim Fehlig 已提交
14091 14092
    } else {
        vshPrint(ctl, "%s", _("Interface attached successfully\n"));
W
Wen Congyang 已提交
14093
        functionReturn = true;
J
Jim Fehlig 已提交
14094
    }
14095 14096 14097 14098

 cleanup:
    if (dom)
        virDomainFree(dom);
14099
    virBufferFreeAndReset(&buf);
W
Wen Congyang 已提交
14100
    return functionReturn;
14101 14102 14103 14104 14105
}

/*
 * "detach-interface" command
 */
14106
static const vshCmdInfo info_detach_interface[] = {
14107 14108
    {"help", N_("detach network interface")},
    {"desc", N_("Detach network interface.")},
14109 14110 14111
    {NULL, NULL}
};

14112
static const vshCmdOptDef opts_detach_interface[] = {
14113 14114 14115
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"type",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")},
    {"mac",    VSH_OT_STRING, 0, N_("MAC address")},
14116 14117
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
14118 14119 14120
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
14121
static bool
14122
cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
14123 14124 14125 14126 14127 14128 14129
{
    virDomainPtr dom = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj=NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr cur = NULL;
    xmlBufferPtr xml_buf = NULL;
14130
    const char *mac =NULL, *type = NULL;
14131
    char *doc;
14132
    char buf[64];
E
Eric Blake 已提交
14133
    int i = 0, diff_mac;
W
Wen Congyang 已提交
14134 14135
    int ret;
    int functionReturn = false;
J
Jim Fehlig 已提交
14136
    unsigned int flags;
14137

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

J
Jim Meyering 已提交
14141
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
14142 14143
        goto cleanup;

14144
    if (vshCommandOptString(cmd, "type", &type) <= 0)
14145 14146
        goto cleanup;

14147 14148
    if (vshCommandOptString(cmd, "mac", &mac) < 0) {
        vshError(ctl, "%s", _("missing option"));
14149
        goto cleanup;
14150
    }
14151 14152 14153 14154 14155

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

14156
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
14157
    VIR_FREE(doc);
14158
    if (!xml) {
14159
        vshError(ctl, "%s", _("Failed to get interface information"));
14160 14161 14162
        goto cleanup;
    }

E
Eric Blake 已提交
14163
    snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
14164 14165 14166
    obj = xmlXPathEval(BAD_CAST buf, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
14167
        vshError(ctl, _("No found interface whose type is %s"), type);
14168 14169 14170
        goto cleanup;
    }

14171 14172 14173 14174 14175 14176
    if ((!mac) && (obj->nodesetval->nodeNr > 1)) {
        vshError(ctl, _("Domain has %d interfaces. Please specify which one "
                        "to detach using --mac"), obj->nodesetval->nodeNr);
        goto cleanup;
    }

14177 14178 14179 14180 14181 14182 14183
    if (!mac)
        goto hit;

    /* search mac */
    for (; i < obj->nodesetval->nodeNr; i++) {
        cur = obj->nodesetval->nodeTab[i]->children;
        while (cur != NULL) {
14184 14185 14186
            if (cur->type == XML_ELEMENT_NODE &&
                xmlStrEqual(cur->name, BAD_CAST "mac")) {
                char *tmp_mac = virXMLPropString(cur, "address");
14187
                diff_mac = virMacAddrCompare(tmp_mac, mac);
14188
                VIR_FREE(tmp_mac);
14189 14190 14191 14192 14193 14194 14195
                if (!diff_mac) {
                    goto hit;
                }
            }
            cur = cur->next;
        }
    }
14196
    vshError(ctl, _("No found interface whose MAC address is %s"), mac);
14197 14198 14199 14200 14201
    goto cleanup;

 hit:
    xml_buf = xmlBufferCreate();
    if (!xml_buf) {
14202
        vshError(ctl, "%s", _("Failed to allocate memory"));
14203 14204 14205
        goto cleanup;
    }

14206
    if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) {
14207
        vshError(ctl, "%s", _("Failed to create XML"));
14208 14209 14210
        goto cleanup;
    }

14211
    if (vshCommandOptBool(cmd, "config")) {
14212
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
14213
        if (virDomainIsActive(dom) == 1)
14214
            flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
14215 14216 14217 14218 14219 14220 14221 14222
        ret = virDomainDetachDeviceFlags(dom,
                                         (char *)xmlBufferContent(xml_buf),
                                         flags);
    } else {
        ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
    }

    if (ret != 0) {
L
Laine Stump 已提交
14223
        vshError(ctl, "%s", _("Failed to detach interface"));
J
Jim Fehlig 已提交
14224
    } else {
J
Jim Meyering 已提交
14225
        vshPrint(ctl, "%s", _("Interface detached successfully\n"));
W
Wen Congyang 已提交
14226
        functionReturn = true;
14227
    }
14228 14229 14230 14231

 cleanup:
    if (dom)
        virDomainFree(dom);
14232
    xmlXPathFreeObject(obj);
14233
    xmlXPathFreeContext(ctxt);
14234 14235
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
W
Wen Congyang 已提交
14236
    return functionReturn;
14237 14238 14239 14240 14241
}

/*
 * "attach-disk" command
 */
14242
static const vshCmdInfo info_attach_disk[] = {
14243 14244
    {"help", N_("attach disk device")},
    {"desc", N_("Attach new disk device.")},
14245 14246 14247
    {NULL, NULL}
};

14248
static const vshCmdOptDef opts_attach_disk[] = {
14249
    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
14250 14251
    {"source",  VSH_OT_DATA, VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK,
     N_("source of disk device")},
14252 14253 14254
    {"target",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
    {"driver",    VSH_OT_STRING, 0, N_("driver of disk device")},
    {"subdriver", VSH_OT_STRING, 0, N_("subdriver of disk device")},
14255
    {"cache",     VSH_OT_STRING, 0, N_("cache mode of disk device")},
14256 14257
    {"type",    VSH_OT_STRING, 0, N_("target device type")},
    {"mode",    VSH_OT_STRING, 0, N_("mode of device reading and writing")},
14258 14259
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
14260
    {"sourcetype", VSH_OT_STRING, 0, N_("type of source (block|file)")},
14261 14262
    {"serial", VSH_OT_STRING, 0, N_("serial of disk device")},
    {"shareable", VSH_OT_BOOL, 0, N_("shareable between domains")},
14263
    {"rawio", VSH_OT_BOOL, 0, N_("needs rawio capability")},
14264
    {"address", VSH_OT_STRING, 0, N_("address of disk device")},
14265 14266
    {"multifunction", VSH_OT_BOOL, 0,
     N_("use multifunction pci under specified address")},
14267 14268 14269
    {NULL, 0, 0, NULL}
};

14270 14271 14272 14273 14274 14275 14276 14277 14278 14279 14280 14281 14282 14283 14284 14285 14286 14287 14288 14289 14290 14291 14292 14293 14294 14295 14296 14297 14298 14299 14300 14301 14302 14303 14304 14305 14306 14307 14308 14309 14310 14311 14312 14313 14314 14315 14316 14317 14318 14319 14320 14321 14322 14323 14324 14325 14326 14327 14328 14329 14330 14331 14332 14333 14334 14335 14336 14337 14338 14339 14340 14341 14342 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 14355 14356 14357 14358 14359 14360 14361 14362 14363 14364 14365 14366 14367 14368 14369 14370 14371 14372 14373 14374 14375 14376 14377 14378 14379 14380 14381 14382 14383 14384 14385 14386 14387 14388 14389 14390 14391 14392 14393 14394 14395 14396 14397 14398 14399 14400 14401 14402 14403 14404 14405 14406 14407 14408 14409 14410 14411 14412 14413 14414 14415 14416
enum {
    DISK_ADDR_TYPE_INVALID,
    DISK_ADDR_TYPE_PCI,
    DISK_ADDR_TYPE_SCSI,
    DISK_ADDR_TYPE_IDE,
};

struct PCIAddress {
    unsigned int domain;
    unsigned int bus;
    unsigned int slot;
    unsigned int function;
};

struct SCSIAddress {
    unsigned int controller;
    unsigned int bus;
    unsigned int unit;
};

struct IDEAddress {
    unsigned int controller;
    unsigned int bus;
    unsigned int unit;
};

struct DiskAddress {
    int type;
    union {
        struct PCIAddress pci;
        struct SCSIAddress scsi;
        struct IDEAddress ide;
    } addr;
};

static int str2PCIAddress(const char *str, struct PCIAddress *pciAddr)
{
    char *domain, *bus, *slot, *function;

    if (!pciAddr)
        return -1;
    if (!str)
        return -1;

    domain = (char *)str;

    if (virStrToLong_ui(domain, &bus, 0, &pciAddr->domain) != 0)
        return -1;

    bus++;
    if (virStrToLong_ui(bus, &slot, 0, &pciAddr->bus) != 0)
        return -1;

    slot++;
    if (virStrToLong_ui(slot, &function, 0, &pciAddr->slot) != 0)
        return -1;

    function++;
    if (virStrToLong_ui(function, NULL, 0, &pciAddr->function) != 0)
        return -1;

    return 0;
}

static int str2SCSIAddress(const char *str, struct SCSIAddress *scsiAddr)
{
    char *controller, *bus, *unit;

    if (!scsiAddr)
        return -1;
    if (!str)
        return -1;

    controller = (char *)str;

    if (virStrToLong_ui(controller, &bus, 0, &scsiAddr->controller) != 0)
        return -1;

    bus++;
    if (virStrToLong_ui(bus, &unit, 0, &scsiAddr->bus) != 0)
        return -1;

    unit++;
    if (virStrToLong_ui(unit, NULL, 0, &scsiAddr->unit) != 0)
        return -1;

    return 0;
}

static int str2IDEAddress(const char *str, struct IDEAddress *ideAddr)
{
    char *controller, *bus, *unit;

    if (!ideAddr)
        return -1;
    if (!str)
        return -1;

    controller = (char *)str;

    if (virStrToLong_ui(controller, &bus, 0, &ideAddr->controller) != 0)
        return -1;

    bus++;
    if (virStrToLong_ui(bus, &unit, 0, &ideAddr->bus) != 0)
        return -1;

    unit++;
    if (virStrToLong_ui(unit, NULL, 0, &ideAddr->unit) != 0)
        return -1;

    return 0;
}

/* pci address pci:0000.00.0x0a.0 (domain:bus:slot:function)
 * ide disk address: ide:00.00.0 (controller:bus:unit)
 * scsi disk address: scsi:00.00.0 (controller:bus:unit)
 */

static int str2DiskAddress(const char *str, struct DiskAddress *diskAddr)
{
    char *type, *addr;

    if (!diskAddr)
        return -1;
    if (!str)
        return -1;

    type = (char *)str;
    addr = strchr(type, ':');
    if (!addr)
        return -1;

    if (STREQLEN(type, "pci", addr - type)) {
        diskAddr->type = DISK_ADDR_TYPE_PCI;
        return str2PCIAddress(addr + 1, &diskAddr->addr.pci);
    } else if (STREQLEN(type, "scsi", addr - type)) {
        diskAddr->type = DISK_ADDR_TYPE_SCSI;
        return str2SCSIAddress(addr + 1, &diskAddr->addr.scsi);
    } else if (STREQLEN(type, "ide", addr - type)) {
        diskAddr->type = DISK_ADDR_TYPE_IDE;
        return str2IDEAddress(addr + 1, &diskAddr->addr.ide);
    }

    return -1;
}

E
Eric Blake 已提交
14417
static bool
14418
cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
14419 14420
{
    virDomainPtr dom = NULL;
14421
    const char *source = NULL, *target = NULL, *driver = NULL,
14422 14423 14424
                *subdriver = NULL, *type = NULL, *mode = NULL,
                *cache = NULL, *serial = NULL, *straddr = NULL;
    struct DiskAddress diskAddr;
W
Wen Congyang 已提交
14425 14426
    bool isFile = false, functionReturn = false;
    int ret;
J
Jim Fehlig 已提交
14427
    unsigned int flags;
14428
    const char *stype = NULL;
14429 14430
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *xml;
O
Osier Yang 已提交
14431
    struct stat st;
14432

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

J
Jim Meyering 已提交
14436
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
14437 14438
        goto cleanup;

14439
    if (vshCommandOptString(cmd, "source", &source) <= 0)
14440
        goto cleanup;
14441 14442 14443 14444
    /* Allow empty string as a placeholder that implies no source, for
     * use in adding a cdrom drive with no disk.  */
    if (!*source)
        source = NULL;
14445

14446
    if (vshCommandOptString(cmd, "target", &target) <= 0)
14447 14448
        goto cleanup;

14449 14450 14451 14452
    if (vshCommandOptString(cmd, "driver", &driver) < 0 ||
        vshCommandOptString(cmd, "subdriver", &subdriver) < 0 ||
        vshCommandOptString(cmd, "type", &type) < 0 ||
        vshCommandOptString(cmd, "mode", &mode) < 0 ||
14453 14454 14455
        vshCommandOptString(cmd, "cache", &cache) < 0 ||
        vshCommandOptString(cmd, "serial", &serial) < 0 ||
        vshCommandOptString(cmd, "address", &straddr) < 0 ||
14456
        vshCommandOptString(cmd, "sourcetype", &stype) < 0) {
14457
        vshError(ctl, "%s", _("missing option"));
14458 14459
        goto cleanup;
    }
14460

14461
    if (!stype) {
O
Osier Yang 已提交
14462
        if (driver && (STREQ(driver, "file") || STREQ(driver, "tap"))) {
E
Eric Blake 已提交
14463
            isFile = true;
O
Osier Yang 已提交
14464 14465 14466 14467
        } else {
            if (source && !stat(source, &st))
                isFile = S_ISREG(st.st_mode) ? true : false;
        }
14468
    } else if (STREQ(stype, "file")) {
E
Eric Blake 已提交
14469
        isFile = true;
14470 14471 14472
    } else if (STRNEQ(stype, "block")) {
        vshError(ctl, _("Unknown source type: '%s'"), stype);
        goto cleanup;
14473 14474 14475
    }

    if (mode) {
14476
        if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
E
Eric Blake 已提交
14477 14478
            vshError(ctl, _("No support for %s in command 'attach-disk'"),
                     mode);
14479 14480 14481 14482 14483
            goto cleanup;
        }
    }

    /* Make XML of disk */
14484
    virBufferAsprintf(&buf, "<disk type='%s'",
14485 14486
                      (isFile) ? "file" : "block");
    if (type)
14487
        virBufferAsprintf(&buf, " device='%s'", type);
14488 14489
    if (vshCommandOptBool (cmd, "rawio"))
        virBufferAddLit(&buf, " rawio='yes'");
14490 14491
    virBufferAddLit(&buf, ">\n");

14492
    if (driver || subdriver)
14493
        virBufferAsprintf(&buf, "  <driver");
14494 14495

    if (driver)
14496
        virBufferAsprintf(&buf, " name='%s'", driver);
14497
    if (subdriver)
14498
        virBufferAsprintf(&buf, " type='%s'", subdriver);
14499 14500
    if (cache)
        virBufferAsprintf(&buf, " cache='%s'", cache);
14501

14502
    if (driver || subdriver || cache)
14503
        virBufferAddLit(&buf, "/>\n");
14504

14505 14506 14507 14508
    if (source)
        virBufferAsprintf(&buf, "  <source %s='%s'/>\n",
                          (isFile) ? "file" : "dev",
                          source);
14509
    virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
14510
    if (mode)
14511
        virBufferAsprintf(&buf, "  <%s/>\n", mode);
14512

14513 14514 14515 14516 14517 14518 14519 14520 14521 14522 14523 14524 14525 14526 14527 14528
    if (serial)
        virBufferAsprintf(&buf, "  <serial>%s</serial>\n", serial);

    if (vshCommandOptBool(cmd, "shareable"))
        virBufferAsprintf(&buf, "  <shareable/>\n");

    if (straddr) {
        if (str2DiskAddress(straddr, &diskAddr) != 0) {
            vshError(ctl, _("Invalid address."));
            goto cleanup;
        }

        if (STRPREFIX((const char *)target, "vd")) {
            if (diskAddr.type == DISK_ADDR_TYPE_PCI) {
                virBufferAsprintf(&buf,
                                  "  <address type='pci' domain='0x%04x'"
14529
                                  " bus ='0x%02x' slot='0x%02x' function='0x%0x'",
14530 14531
                                  diskAddr.addr.pci.domain, diskAddr.addr.pci.bus,
                                  diskAddr.addr.pci.slot, diskAddr.addr.pci.function);
14532 14533 14534
                if (vshCommandOptBool(cmd, "multifunction"))
                    virBufferAddLit(&buf, " multifunction='on'");
                virBufferAddLit(&buf, "/>\n");
14535 14536 14537 14538 14539 14540 14541 14542 14543 14544 14545 14546 14547 14548 14549 14550 14551 14552 14553 14554 14555 14556 14557 14558 14559 14560 14561 14562 14563
            } else {
                vshError(ctl, "%s", _("expecting a pci:0000.00.00.00 address."));
                goto cleanup;
            }
        } else if (STRPREFIX((const char *)target, "sd")) {
            if (diskAddr.type == DISK_ADDR_TYPE_SCSI) {
                virBufferAsprintf(&buf,
                                  "  <address type='drive' controller='%d'"
                                  " bus='%d' unit='%d' />\n",
                                  diskAddr.addr.scsi.controller, diskAddr.addr.scsi.bus,
                                  diskAddr.addr.scsi.unit);
            } else {
                vshError(ctl, "%s", _("expecting a scsi:00.00.00 address."));
                goto cleanup;
            }
        } else if (STRPREFIX((const char *)target, "hd")) {
            if (diskAddr.type == DISK_ADDR_TYPE_IDE) {
                virBufferAsprintf(&buf,
                                  "  <address type='drive' controller='%d'"
                                  " bus='%d' unit='%d' />\n",
                                  diskAddr.addr.ide.controller, diskAddr.addr.ide.bus,
                                  diskAddr.addr.ide.unit);
            } else {
                vshError(ctl, "%s", _("expecting an ide:00.00.00 address."));
                goto cleanup;
            }
        }
    }

14564
    virBufferAddLit(&buf, "</disk>\n");
14565

14566 14567
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
14568
        return false;
14569 14570
    }

14571
    xml = virBufferContentAndReset(&buf);
14572

14573
    if (vshCommandOptBool(cmd, "config")) {
14574
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
14575
        if (virDomainIsActive(dom) == 1)
14576
            flags |= VIR_DOMAIN_AFFECT_LIVE;
14577
        ret = virDomainAttachDeviceFlags(dom, xml, flags);
J
Jim Fehlig 已提交
14578
    } else {
14579
        ret = virDomainAttachDevice(dom, xml);
J
Jim Fehlig 已提交
14580
    }
14581

14582 14583
    VIR_FREE(xml);

J
Jim Fehlig 已提交
14584
    if (ret != 0) {
L
Laine Stump 已提交
14585
        vshError(ctl, "%s", _("Failed to attach disk"));
J
Jim Fehlig 已提交
14586 14587
    } else {
        vshPrint(ctl, "%s", _("Disk attached successfully\n"));
W
Wen Congyang 已提交
14588
        functionReturn = true;
J
Jim Fehlig 已提交
14589
    }
14590 14591 14592 14593

 cleanup:
    if (dom)
        virDomainFree(dom);
14594
    virBufferFreeAndReset(&buf);
W
Wen Congyang 已提交
14595
    return functionReturn;
14596 14597
}

14598 14599 14600 14601 14602 14603 14604 14605 14606 14607 14608 14609 14610 14611 14612 14613 14614 14615 14616 14617 14618 14619 14620 14621 14622 14623 14624 14625 14626 14627 14628 14629 14630 14631 14632 14633 14634 14635 14636 14637 14638 14639 14640 14641 14642 14643 14644 14645 14646 14647 14648 14649 14650 14651 14652 14653 14654 14655 14656 14657 14658 14659 14660 14661 14662 14663 14664 14665 14666 14667 14668 14669 14670 14671 14672 14673 14674 14675 14676 14677 14678 14679 14680 14681 14682 14683 14684 14685 14686 14687 14688 14689 14690 14691 14692 14693 14694 14695 14696 14697 14698 14699 14700 14701 14702 14703 14704 14705 14706 14707 14708 14709 14710 14711 14712 14713 14714 14715 14716 14717 14718 14719 14720 14721 14722 14723 14724 14725 14726 14727 14728 14729 14730 14731 14732 14733 14734 14735 14736 14737 14738 14739 14740 14741 14742 14743 14744 14745 14746 14747 14748 14749 14750 14751 14752 14753 14754 14755 14756 14757 14758 14759 14760 14761 14762 14763 14764 14765 14766 14767 14768 14769 14770 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 14788 14789 14790 14791 14792 14793 14794 14795 14796 14797 14798 14799 14800 14801 14802 14803 14804 14805 14806 14807 14808 14809 14810
typedef enum {
    VSH_FIND_DISK_NORMAL,
    VSH_FIND_DISK_CHANGEABLE,
} vshFindDiskType;

/* Helper function to find disk device in XML doc.  Returns the disk
 * node on success, or NULL on failure. Caller must free the result
 * @path: Fully-qualified path or target of disk device.
 * @type: Either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE.
 */
static xmlNodePtr
vshFindDisk(const char *doc,
            const char *path,
            int type)
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj= NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr cur = NULL;
    xmlNodePtr ret = NULL;
    int i = 0;

    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
    if (!xml) {
        vshError(NULL, "%s", _("Failed to get disk information"));
        goto cleanup;
    }

    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
    if ((obj == NULL) ||
        (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) ||
        (obj->nodesetval->nodeNr == 0)) {
        vshError(NULL, "%s", _("Failed to get disk information"));
        goto cleanup;
    }

    /* search disk using @path */
    for (; i < obj->nodesetval->nodeNr; i++) {
        bool is_supported = true;

        if (type == VSH_FIND_DISK_CHANGEABLE) {
            xmlNodePtr n = obj->nodesetval->nodeTab[i];
            is_supported = false;

            /* Check if the disk is CDROM or floppy disk */
            if (xmlStrEqual(n->name, BAD_CAST "disk")) {
                char *device_value = virXMLPropString(n, "device");

                if (STREQ(device_value, "cdrom") ||
                    STREQ(device_value, "floppy"))
                    is_supported = true;

                VIR_FREE(device_value);
            }

            if (!is_supported)
                continue;
        }

        cur = obj->nodesetval->nodeTab[i]->children;
        while (cur != NULL) {
            if (cur->type == XML_ELEMENT_NODE) {
                char *tmp = NULL;

                if (xmlStrEqual(cur->name, BAD_CAST "source")) {
                    if ((tmp = virXMLPropString(cur, "file")) ||
                        (tmp = virXMLPropString(cur, "dev")) ||
                        (tmp = virXMLPropString(cur, "dir")) ||
                        (tmp = virXMLPropString(cur, "name"))) {
                    }
                } else if (xmlStrEqual(cur->name, BAD_CAST "target")) {
                    tmp = virXMLPropString(cur, "dev");
                }

                if (STREQ_NULLABLE(tmp, path)) {
                    ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1);
                    VIR_FREE(tmp);
                    goto cleanup;
                }
                VIR_FREE(tmp);
            }
            cur = cur->next;
        }
    }

    vshError(NULL, _("No found disk whose source path or target is %s"), path);

cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
    return ret;
}

typedef enum {
    VSH_PREPARE_DISK_XML_NONE = 0,
    VSH_PREPARE_DISK_XML_EJECT,
    VSH_PREPARE_DISK_XML_INSERT,
    VSH_PREPARE_DISK_XML_UPDATE,
} vshPrepareDiskXMLType;

/* Helper function to prepare disk XML. Could be used for disk
 * detaching, media changing(ejecting, inserting, updating)
 * for changeable disk. Returns the processed XML as string on
 * success, or NULL on failure. Caller must free the result.
 */
static char *
vshPrepareDiskXML(xmlNodePtr disk_node,
                  const char *source,
                  const char *path,
                  int type)
{
    xmlNodePtr cur = NULL;
    xmlBufferPtr xml_buf = NULL;
    const char *disk_type = NULL;
    const char *device_type = NULL;
    xmlNodePtr new_node = NULL;
    char *ret = NULL;

    if (!disk_node)
        return NULL;

    xml_buf = xmlBufferCreate();
    if (!xml_buf) {
        vshError(NULL, "%s", _("Failed to allocate memory"));
        return NULL;
    }

    device_type = virXMLPropString(disk_node, "device");

    if (STREQ_NULLABLE(device_type, "cdrom") ||
        STREQ_NULLABLE(device_type, "floppy")) {
        bool has_source = false;
        disk_type = virXMLPropString(disk_node, "type");

        cur = disk_node->children;
        while (cur != NULL) {
            if (cur->type == XML_ELEMENT_NODE &&
                xmlStrEqual(cur->name, BAD_CAST "source")) {
                has_source = true;
                break;
            }
            cur = cur->next;
        }

        if (!has_source) {
            if (type == VSH_PREPARE_DISK_XML_EJECT) {
                vshError(NULL, _("The disk device '%s' doesn't have media"),
                         path);
                goto error;
            }

            if (source) {
                new_node = xmlNewNode(NULL, BAD_CAST "source");
                xmlNewProp(new_node, (const xmlChar *)disk_type,
                           (const xmlChar *)source);
                xmlAddChild(disk_node, new_node);
            } else if (type == VSH_PREPARE_DISK_XML_INSERT) {
                vshError(NULL, _("No source is specified for inserting media"));
                goto error;
            } else if (type == VSH_PREPARE_DISK_XML_UPDATE) {
                vshError(NULL, _("No source is specified for updating media"));
                goto error;
            }
        }

        if (has_source) {
            if (type == VSH_PREPARE_DISK_XML_INSERT) {
                vshError(NULL, _("The disk device '%s' already has media"),
                         path);
                goto error;
            }

            /* Remove the source if it tends to eject/update media. */
            xmlUnlinkNode(cur);
            xmlFreeNode(cur);

            if (source && (type == VSH_PREPARE_DISK_XML_UPDATE)) {
                new_node = xmlNewNode(NULL, BAD_CAST "source");
                xmlNewProp(new_node, (const xmlChar *)disk_type,
                           (const xmlChar *)source);
                xmlAddChild(disk_node, new_node);
            }
        }
    }

    if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0) < 0) {
        vshError(NULL, "%s", _("Failed to create XML"));
        goto error;
    }

    goto cleanup;

cleanup:
    VIR_FREE(device_type);
    VIR_FREE(disk_type);
    if (xml_buf) {
        if (VIR_ALLOC_N(ret, xmlBufferLength(xml_buf)) < 0) {
            virReportOOMError();
            return NULL;
        }
        memcpy(ret, (char *)xmlBufferContent(xml_buf), xmlBufferLength(xml_buf));
        xmlBufferFree(xml_buf);
    }
    return ret;

error:
    xmlBufferFree(xml_buf);
    xml_buf = NULL;
    goto cleanup;
}

14811 14812 14813
/*
 * "detach-disk" command
 */
14814
static const vshCmdInfo info_detach_disk[] = {
14815 14816
    {"help", N_("detach disk device")},
    {"desc", N_("Detach disk device.")},
14817 14818 14819
    {NULL, NULL}
};

14820
static const vshCmdOptDef opts_detach_disk[] = {
14821 14822
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
14823 14824
    {"persistent", VSH_OT_ALIAS, 0, "config"},
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
14825 14826 14827
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
14828
static bool
14829
cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
14830
{
14831
    char *disk_xml = NULL;
14832
    virDomainPtr dom = NULL;
14833
    const char *target = NULL;
A
Alex Jia 已提交
14834
    char *doc = NULL;
W
Wen Congyang 已提交
14835 14836
    int ret;
    bool functionReturn = false;
J
Jim Fehlig 已提交
14837
    unsigned int flags;
14838
    xmlNodePtr disk_node = NULL;
14839

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

J
Jim Meyering 已提交
14843
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
14844 14845
        goto cleanup;

14846
    if (vshCommandOptString(cmd, "target", &target) <= 0)
14847 14848 14849 14850 14851 14852
        goto cleanup;

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

14853
    if (!(disk_node = vshFindDisk(doc, target, VSH_FIND_DISK_NORMAL)))
14854 14855
        goto cleanup;

14856 14857
    if (!(disk_xml = vshPrepareDiskXML(disk_node, NULL, NULL,
                                       VSH_PREPARE_DISK_XML_NONE)))
14858 14859
        goto cleanup;

14860
    if (vshCommandOptBool(cmd, "config")) {
14861
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
14862
        if (virDomainIsActive(dom) == 1)
14863
            flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
14864
        ret = virDomainDetachDeviceFlags(dom,
14865
                                         disk_xml,
J
Jim Fehlig 已提交
14866 14867
                                         flags);
    } else {
14868
        ret = virDomainDetachDevice(dom, disk_xml);
J
Jim Fehlig 已提交
14869 14870 14871
    }

    if (ret != 0) {
L
Laine Stump 已提交
14872
        vshError(ctl, "%s", _("Failed to detach disk"));
J
Jim Fehlig 已提交
14873
    } else {
J
Jim Meyering 已提交
14874
        vshPrint(ctl, "%s", _("Disk detached successfully\n"));
W
Wen Congyang 已提交
14875
        functionReturn = true;
14876
    }
14877 14878

 cleanup:
14879 14880 14881
    xmlFreeNode(disk_node);
    VIR_FREE(disk_xml);
    VIR_FREE(doc);
14882 14883
    if (dom)
        virDomainFree(dom);
W
Wen Congyang 已提交
14884
    return functionReturn;
14885 14886
}

O
Osier Yang 已提交
14887 14888 14889 14890 14891 14892 14893 14894 14895 14896 14897 14898 14899 14900 14901 14902 14903 14904 14905 14906 14907 14908 14909 14910 14911 14912 14913 14914 14915 14916 14917 14918 14919 14920 14921 14922 14923 14924 14925 14926 14927 14928 14929 14930 14931 14932 14933 14934 14935 14936 14937 14938 14939 14940 14941 14942 14943 14944 14945 14946 14947 14948 14949 14950 14951 14952 14953 14954 14955 14956 14957 14958 14959 14960 14961 14962 14963 14964 14965 14966 14967 14968 14969 14970 14971 14972 14973 14974 14975 14976 14977 14978 14979 14980 14981 14982 14983 14984 14985 14986 14987 14988 14989 14990 14991 14992 14993 14994 14995 14996 14997 14998 14999 15000 15001 15002 15003 15004 15005 15006 15007 15008 15009 15010 15011 15012 15013 15014 15015 15016 15017 15018 15019
/*
 * "change-media" command
 */
static const vshCmdInfo info_change_media[] = {
    {"help", N_("Change media of CD or floppy drive")},
    {"desc", N_("Change media of CD or floppy drive.")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_change_media[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path or "
                                            "target of disk device")},
    {"source", VSH_OT_DATA, 0, N_("source of the media")},
    {"eject", VSH_OT_BOOL, 0, N_("Eject the media")},
    {"insert", VSH_OT_BOOL, 0, N_("Insert the media")},
    {"update", VSH_OT_BOOL, 0, N_("Update the media")},
    {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and --config, "
                                   "depends on implementation of hypervisor driver")},
    {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running domain")},
    {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect observed on next boot")},
    {"force",  VSH_OT_BOOL, 0, N_("force media insertion")},
    {NULL, 0, 0, NULL}
};

static bool
cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    const char *source = NULL;
    const char *path = NULL;
    const char *doc = NULL;
    xmlNodePtr disk_node = NULL;
    const char *disk_xml = NULL;
    int flags = 0;
    bool config, live, current, force = false;
    bool eject, insert, update = false;
    bool ret = false;
    int prepare_type = 0;
    const char *action = NULL;

    config = vshCommandOptBool(cmd, "config");
    live = vshCommandOptBool(cmd, "live");
    current = vshCommandOptBool(cmd, "current");
    force = vshCommandOptBool(cmd, "force");
    eject = vshCommandOptBool(cmd, "eject");
    insert = vshCommandOptBool(cmd, "insert");
    update = vshCommandOptBool(cmd, "update");

    if (eject + insert + update > 1) {
        vshError(ctl, "%s", _("--eject, --insert, and --update must be specified "
                            "exclusively."));
        return false;
    }

    if (eject) {
        prepare_type = VSH_PREPARE_DISK_XML_EJECT;
        action = "eject";
    }

    if (insert) {
        prepare_type = VSH_PREPARE_DISK_XML_INSERT;
        action = "insert";
    }

    if (update || (!eject && !insert)) {
        prepare_type = VSH_PREPARE_DISK_XML_UPDATE;
        action = "update";
    }

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
            return false;
        }
        flags = VIR_DOMAIN_AFFECT_CURRENT;
    } else {
        if (config)
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
        if (live)
            flags |= VIR_DOMAIN_AFFECT_LIVE;
    }

    if (force)
        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;

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

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

    if (vshCommandOptString(cmd, "path", &path) <= 0)
        goto cleanup;

    if (vshCommandOptString(cmd, "source", &source) < 0)
        goto cleanup;

    if (insert && !source) {
        vshError(ctl, "%s", _("No disk source specified for inserting"));
        goto cleanup;
    }

    if (flags & VIR_DOMAIN_AFFECT_CONFIG)
        doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
    else
        doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

    if (!(disk_node = vshFindDisk(doc, path, VSH_FIND_DISK_CHANGEABLE)))
        goto cleanup;

    if (!(disk_xml = vshPrepareDiskXML(disk_node, source, path, prepare_type)))
        goto cleanup;

    if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) {
        vshError(ctl, _("Failed to complete action %s on media"), action);
        goto cleanup;
    }

    vshPrint(ctl, _("succeeded to complete action %s on media\n"), action);
    ret = true;

cleanup:
    VIR_FREE(doc);
    xmlFreeNode(disk_node);
    VIR_FREE(disk_xml);
    if (dom)
        virDomainFree(dom);
    return ret;
}

15020 15021 15022 15023
/*
 * "cpu-compare" command
 */
static const vshCmdInfo info_cpu_compare[] = {
15024 15025
    {"help", N_("compare host CPU with a CPU described by an XML file")},
    {"desc", N_("compare CPU with host CPU")},
15026 15027 15028 15029
    {NULL, NULL}
};

static const vshCmdOptDef opts_cpu_compare[] = {
15030
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML CPU description")},
15031 15032 15033
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15034
static bool
15035 15036
cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
{
15037
    const char *from = NULL;
15038
    bool ret = false;
15039 15040
    char *buffer;
    int result;
15041 15042 15043 15044 15045 15046
    const char *snippet;

    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlBufferPtr xml_buf = NULL;
    xmlNodePtr node;
15047

15048
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
15049
        return false;
15050

15051
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
15052
        return false;
15053

15054 15055 15056
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
        vshError(ctl, _("Failed to read file '%s' to compare"),
                 from);
E
Eric Blake 已提交
15057
        return false;
15058
    }
15059

15060 15061 15062 15063 15064 15065 15066 15067 15068 15069 15070 15071 15072 15073 15074 15075 15076 15077 15078 15079 15080 15081 15082 15083 15084
    /* try to extract the CPU element from as it would appear in a domain XML*/
    if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
        goto cleanup;

    if ((node = virXPathNode("/cpu|"
                             "/domain/cpu|"
                              "/capabilities/host/cpu", ctxt))) {
        if (!(xml_buf = xmlBufferCreate())) {
            vshError(ctl, _("Can't create XML buffer to extract CPU element."));
            goto cleanup;
        }

        if (xmlNodeDump(xml_buf, xml, node, 0, 0) < 0) {
            vshError(ctl, _("Failed to extract CPU element snippet from domain XML."));
            goto cleanup;
        }

        snippet = (const char *) xmlBufferContent(xml_buf);
    } else {
        vshError(ctl, _("File '%s' does not contain a <cpu> element or is not "
                        "a valid domain or capabilities XML"), from);
        goto cleanup;
    }

    result = virConnectCompareCPU(ctl->conn, snippet, 0);
15085 15086 15087 15088 15089

    switch (result) {
    case VIR_CPU_COMPARE_INCOMPATIBLE:
        vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"),
                 from);
15090
        goto cleanup;
15091 15092 15093 15094 15095 15096 15097 15098 15099 15100 15101 15102 15103 15104 15105
        break;

    case VIR_CPU_COMPARE_IDENTICAL:
        vshPrint(ctl, _("CPU described in %s is identical to host CPU\n"),
                 from);
        break;

    case VIR_CPU_COMPARE_SUPERSET:
        vshPrint(ctl, _("Host CPU is a superset of CPU described in %s\n"),
                 from);
        break;

    case VIR_CPU_COMPARE_ERROR:
    default:
        vshError(ctl, _("Failed to compare host CPU with %s"), from);
15106
        goto cleanup;
15107 15108
    }

15109 15110 15111 15112 15113 15114 15115 15116
    ret = true;

cleanup:
    VIR_FREE(buffer);
    xmlBufferFree(xml_buf);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);

15117 15118 15119
    return ret;
}

15120 15121 15122 15123
/*
 * "cpu-baseline" command
 */
static const vshCmdInfo info_cpu_baseline[] = {
15124 15125
    {"help", N_("compute baseline CPU")},
    {"desc", N_("Compute baseline CPU for a set of given CPUs.")},
15126 15127 15128 15129
    {NULL, NULL}
};

static const vshCmdOptDef opts_cpu_baseline[] = {
15130
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing XML CPU descriptions")},
15131 15132 15133
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15134
static bool
15135 15136
cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
{
15137
    const char *from = NULL;
15138
    bool ret = false;
15139 15140 15141
    char *buffer;
    char *result = NULL;
    const char **list = NULL;
15142 15143 15144 15145
    int count = 0;

    xmlDocPtr xml = NULL;
    xmlNodePtr *node_list = NULL;
15146
    xmlXPathContextPtr ctxt = NULL;
15147 15148 15149
    xmlBufferPtr xml_buf = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    int i;
15150

15151
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
15152
        return false;
15153

15154
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
15155
        return false;
15156 15157

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

E
Eric Blake 已提交
15160
    /* add a separate container around the xml */
15161 15162
    virBufferStrcat(&buf, "<container>", buffer, "</container>", NULL);
    if (virBufferError(&buf))
15163 15164
        goto no_memory;

15165 15166 15167 15168 15169 15170 15171 15172 15173 15174 15175 15176 15177
    VIR_FREE(buffer);
    buffer = virBufferContentAndReset(&buf);


    if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
        goto cleanup;

    if ((count = virXPathNodeSet("//cpu[not(ancestor::cpus)]",
                                 ctxt, &node_list)) == -1)
        goto cleanup;

    if (count == 0) {
        vshError(ctl, _("No host CPU specified in '%s'"), from);
15178 15179 15180
        goto cleanup;
    }

15181
    list = vshCalloc(ctl, count, sizeof(const char *));
15182

15183
    if (!(xml_buf = xmlBufferCreate()))
15184 15185
        goto no_memory;

15186 15187
    for (i = 0; i < count; i++) {
        xmlBufferEmpty(xml_buf);
15188

15189 15190 15191
        if (xmlNodeDump(xml_buf, xml,  node_list[i], 0, 0) < 0) {
            vshError(ctl, _("Failed to extract <cpu> element"));
            goto cleanup;
15192 15193
        }

15194
        list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
15195 15196 15197 15198
    }

    result = virConnectBaselineCPU(ctl->conn, list, count, 0);

15199
    if (result) {
15200
        vshPrint(ctl, "%s", result);
15201 15202
        ret = true;
    }
15203 15204 15205

cleanup:
    xmlXPathFreeContext(ctxt);
15206 15207
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
15208 15209
    VIR_FREE(result);
    if ((list != NULL) && (count > 0)) {
15210
        for (i = 0; i < count; i++)
15211 15212 15213 15214 15215 15216 15217 15218 15219
            VIR_FREE(list[i]);
    }
    VIR_FREE(list);
    VIR_FREE(buffer);

    return ret;

no_memory:
    vshError(ctl, "%s", _("Out of memory"));
E
Eric Blake 已提交
15220
    ret = false;
15221
    goto cleanup;
15222 15223
}

15224 15225 15226 15227 15228 15229 15230 15231
/* Common code for the edit / net-edit / pool-edit functions which follow. */
static char *
editWriteToTempFile (vshControl *ctl, const char *doc)
{
    char *ret;
    const char *tmpdir;
    int fd;

15232
    ret = vshMalloc(ctl, PATH_MAX);
15233 15234 15235

    tmpdir = getenv ("TMPDIR");
    if (!tmpdir) tmpdir = "/tmp";
15236 15237
    snprintf (ret, PATH_MAX, "%s/virshXXXXXX.xml", tmpdir);
    fd = mkstemps(ret, 4);
15238
    if (fd == -1) {
15239
        vshError(ctl, _("mkstemps: failed to create temporary file: %s"),
15240
                 strerror(errno));
15241
        VIR_FREE(ret);
15242 15243 15244 15245
        return NULL;
    }

    if (safewrite (fd, doc, strlen (doc)) == -1) {
15246 15247
        vshError(ctl, _("write: %s: failed to write to temporary file: %s"),
                 ret, strerror(errno));
S
Stefan Berger 已提交
15248
        VIR_FORCE_CLOSE(fd);
15249
        unlink (ret);
15250
        VIR_FREE(ret);
15251 15252
        return NULL;
    }
S
Stefan Berger 已提交
15253
    if (VIR_CLOSE(fd) < 0) {
15254 15255
        vshError(ctl, _("close: %s: failed to write or close temporary file: %s"),
                 ret, strerror(errno));
15256
        unlink (ret);
15257
        VIR_FREE(ret);
15258 15259 15260 15261 15262 15263 15264 15265 15266 15267 15268 15269 15270 15271 15272
        return NULL;
    }

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

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

static int
editFile (vshControl *ctl, const char *filename)
{
    const char *editor;
E
Eric Blake 已提交
15273 15274 15275 15276
    virCommandPtr cmd;
    int ret = -1;
    int outfd = STDOUT_FILENO;
    int errfd = STDERR_FILENO;
15277

15278
    editor = getenv ("VISUAL");
E
Eric Blake 已提交
15279 15280 15281 15282
    if (!editor)
        editor = getenv ("EDITOR");
    if (!editor)
        editor = "vi"; /* could be cruel & default to ed(1) here */
15283

15284 15285 15286 15287 15288
    /* 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 已提交
15289 15290
     * is why sudo scrubs it by default).  Conversely, if the editor
     * is safe, we can run it directly rather than wasting a shell.
15291
     */
E
Eric Blake 已提交
15292 15293 15294 15295 15296 15297 15298 15299 15300 15301 15302 15303
    if (strspn (editor, ACCEPTED_CHARS) != strlen (editor)) {
        if (strspn (filename, ACCEPTED_CHARS) != strlen (filename)) {
            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);
15304 15305
    }

E
Eric Blake 已提交
15306 15307 15308 15309 15310 15311 15312
    virCommandSetInputFD(cmd, STDIN_FILENO);
    virCommandSetOutputFD(cmd, &outfd);
    virCommandSetErrorFD(cmd, &errfd);
    if (virCommandRunAsync(cmd, NULL) < 0 ||
        virCommandWait(cmd, NULL) < 0) {
        virshReportError(ctl);
        goto cleanup;
15313
    }
E
Eric Blake 已提交
15314
    ret = 0;
15315

E
Eric Blake 已提交
15316 15317 15318
cleanup:
    virCommandFree(cmd);
    return ret;
15319 15320 15321 15322 15323 15324 15325 15326
}

static char *
editReadBackFile (vshControl *ctl, const char *filename)
{
    char *ret;

    if (virFileReadAll (filename, VIRSH_MAX_XML_FILE, &ret) == -1) {
15327
        vshError(ctl,
15328
                 _("%s: failed to read temporary file: %s"),
15329
                 filename, strerror(errno));
15330 15331 15332 15333 15334
        return NULL;
    }
    return ret;
}

15335

P
Paolo Bonzini 已提交
15336 15337 15338 15339
/*
 * "cd" command
 */
static const vshCmdInfo info_cd[] = {
15340 15341
    {"help", N_("change the current directory")},
    {"desc", N_("Change the current directory.")},
P
Paolo Bonzini 已提交
15342 15343 15344 15345
    {NULL, NULL}
};

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

E
Eric Blake 已提交
15350
static bool
15351
cmdCd(vshControl *ctl, const vshCmd *cmd)
P
Paolo Bonzini 已提交
15352
{
15353
    const char *dir = NULL;
15354
    char *dir_malloced = NULL;
E
Eric Blake 已提交
15355
    bool ret = true;
P
Paolo Bonzini 已提交
15356 15357

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

15362
    if (vshCommandOptString(cmd, "dir", &dir) <= 0) {
P
Paolo Bonzini 已提交
15363
        uid_t uid = geteuid();
15364
        dir = dir_malloced = virGetUserDirectory(uid);
P
Paolo Bonzini 已提交
15365 15366 15367 15368
    }
    if (!dir)
        dir = "/";

P
Phil Petty 已提交
15369
    if (chdir(dir) == -1) {
15370
        vshError(ctl, _("cd: %s: %s"), strerror(errno), dir);
E
Eric Blake 已提交
15371
        ret = false;
P
Paolo Bonzini 已提交
15372 15373
    }

15374
    VIR_FREE(dir_malloced);
P
Phil Petty 已提交
15375
    return ret;
P
Paolo Bonzini 已提交
15376 15377 15378 15379 15380 15381
}

/*
 * "pwd" command
 */
static const vshCmdInfo info_pwd[] = {
15382 15383
    {"help", N_("print the current directory")},
    {"desc", N_("Print the current directory.")},
P
Paolo Bonzini 已提交
15384 15385 15386
    {NULL, NULL}
};

E
Eric Blake 已提交
15387
static bool
P
Paolo Bonzini 已提交
15388 15389 15390
cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *cwd;
15391
    bool ret = true;
P
Paolo Bonzini 已提交
15392

15393 15394
    cwd = getcwd(NULL, 0);
    if (!cwd) {
15395 15396
        vshError(ctl, _("pwd: cannot get current directory: %s"),
                 strerror(errno));
15397 15398
        ret = false;
    } else {
P
Paolo Bonzini 已提交
15399
        vshPrint (ctl, _("%s\n"), cwd);
15400 15401
        VIR_FREE(cwd);
    }
P
Paolo Bonzini 已提交
15402

15403
    return ret;
P
Paolo Bonzini 已提交
15404 15405
}

E
Eric Blake 已提交
15406 15407 15408 15409 15410 15411 15412 15413 15414 15415 15416 15417
/*
 * "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 已提交
15418
    {"str", VSH_OT_ALIAS, 0, "string"},
15419
    {"string", VSH_OT_ARGV, 0, N_("arguments to echo")},
E
Eric Blake 已提交
15420 15421 15422 15423 15424 15425
    {NULL, 0, 0, NULL}
};

/* Exists mainly for debugging virsh, but also handy for adding back
 * quotes for later evaluation.
 */
E
Eric Blake 已提交
15426
static bool
15427
cmdEcho (vshControl *ctl, const vshCmd *cmd)
E
Eric Blake 已提交
15428 15429 15430 15431
{
    bool shell = false;
    bool xml = false;
    int count = 0;
15432
    const vshCmdOpt *opt = NULL;
E
Eric Blake 已提交
15433 15434 15435 15436 15437 15438 15439 15440
    char *arg;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

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

15441
    while ((opt = vshCommandOptArgv(cmd, opt))) {
15442 15443
        char *str;
        virBuffer xmlbuf = VIR_BUFFER_INITIALIZER;
E
Eric Blake 已提交
15444

15445
        arg = opt->data;
15446

E
Eric Blake 已提交
15447 15448
        if (count)
            virBufferAddChar(&buf, ' ');
15449

E
Eric Blake 已提交
15450
        if (xml) {
15451 15452 15453 15454
            virBufferEscapeString(&xmlbuf, "%s", arg);
            if (virBufferError(&buf)) {
                vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
                return false;
E
Eric Blake 已提交
15455
            }
15456 15457 15458
            str = virBufferContentAndReset(&xmlbuf);
        } else {
            str = vshStrdup(ctl, arg);
E
Eric Blake 已提交
15459
        }
15460 15461 15462 15463 15464

        if (shell)
            virBufferEscapeShell(&buf, str);
        else
            virBufferAdd(&buf, str, -1);
E
Eric Blake 已提交
15465
        count++;
15466
        VIR_FREE(str);
E
Eric Blake 已提交
15467 15468 15469 15470
    }

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
15471
        return false;
E
Eric Blake 已提交
15472 15473 15474 15475 15476
    }
    arg = virBufferContentAndReset(&buf);
    if (arg)
        vshPrint(ctl, "%s", arg);
    VIR_FREE(arg);
E
Eric Blake 已提交
15477
    return true;
E
Eric Blake 已提交
15478 15479
}

15480 15481 15482 15483
/*
 * "edit" command
 */
static const vshCmdInfo info_edit[] = {
15484 15485
    {"help", N_("edit XML configuration for a domain")},
    {"desc", N_("Edit the XML configuration for a domain.")},
15486 15487 15488 15489
    {NULL, NULL}
};

static const vshCmdOptDef opts_edit[] = {
15490
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
15491 15492 15493 15494 15495 15496
    {NULL, 0, 0, NULL}
};

/* This function also acts as a template to generate cmdNetworkEdit
 * and cmdPoolEdit functions (below) using a sed script in the Makefile.
 */
E
Eric Blake 已提交
15497
static bool
15498 15499
cmdEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
15500
    bool ret = false;
15501 15502 15503 15504 15505
    virDomainPtr dom = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;
E
Eric Blake 已提交
15506
    unsigned int flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
15507

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

J
Jim Meyering 已提交
15511
    dom = vshCommandOptDomain (ctl, cmd, NULL);
15512 15513 15514 15515
    if (dom == NULL)
        goto cleanup;

    /* Get the XML configuration of the domain. */
15516
    doc = virDomainGetXMLDesc (dom, flags);
15517 15518 15519 15520 15521 15522 15523 15524 15525 15526 15527 15528 15529 15530 15531 15532 15533 15534
    if (!doc)
        goto cleanup;

    /* Create and open the temporary file. */
    tmp = editWriteToTempFile (ctl, doc);
    if (!tmp) goto cleanup;

    /* Start the editor. */
    if (editFile (ctl, tmp) == -1) goto cleanup;

    /* Read back the edited file. */
    doc_edited = editReadBackFile (ctl, tmp);
    if (!doc_edited) goto cleanup;

    /* Compare original XML with edited.  Has it changed at all? */
    if (STREQ (doc, doc_edited)) {
        vshPrint (ctl, _("Domain %s XML configuration not changed.\n"),
                  virDomainGetName (dom));
E
Eric Blake 已提交
15535
        ret = true;
15536 15537 15538 15539 15540 15541 15542
        goto cleanup;
    }

    /* Now re-read the domain XML.  Did someone else change it while
     * it was being edited?  This also catches problems such as us
     * losing a connection or the domain going away.
     */
15543
    doc_reread = virDomainGetXMLDesc (dom, flags);
15544 15545 15546 15547
    if (!doc_reread)
        goto cleanup;

    if (STRNEQ (doc, doc_reread)) {
15548 15549
        vshError(ctl,
                 "%s", _("ERROR: the XML configuration was changed by another user"));
15550 15551 15552 15553 15554 15555 15556 15557 15558 15559 15560 15561
        goto cleanup;
    }

    /* Everything checks out, so redefine the domain. */
    virDomainFree (dom);
    dom = virDomainDefineXML (ctl->conn, doc_edited);
    if (!dom)
        goto cleanup;

    vshPrint (ctl, _("Domain %s XML configuration edited.\n"),
              virDomainGetName(dom));

E
Eric Blake 已提交
15562
    ret = true;
15563 15564 15565 15566 15567

 cleanup:
    if (dom)
        virDomainFree (dom);

15568 15569 15570
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);
15571 15572 15573

    if (tmp) {
        unlink (tmp);
15574
        VIR_FREE(tmp);
15575 15576 15577 15578 15579
    }

    return ret;
}

15580

15581 15582 15583 15584
/*
 * "net-edit" command
 */
static const vshCmdInfo info_network_edit[] = {
15585 15586
    {"help", N_("edit XML configuration for a network")},
    {"desc", N_("Edit the XML configuration for a network.")},
15587 15588 15589 15590
    {NULL, NULL}
};

static const vshCmdOptDef opts_network_edit[] = {
15591
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
15592 15593 15594 15595 15596 15597 15598 15599 15600 15601
    {NULL, 0, 0, NULL}
};

/* This is generated from this file by a sed script in the Makefile. */
#include "virsh-net-edit.c"

/*
 * "pool-edit" command
 */
static const vshCmdInfo info_pool_edit[] = {
15602 15603
    {"help", N_("edit XML configuration for a storage pool")},
    {"desc", N_("Edit the XML configuration for a storage pool.")},
15604 15605 15606 15607
    {NULL, NULL}
};

static const vshCmdOptDef opts_pool_edit[] = {
15608
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
15609 15610 15611 15612 15613 15614
    {NULL, 0, 0, NULL}
};

/* This is generated from this file by a sed script in the Makefile. */
#include "virsh-pool-edit.c"

K
Karel Zak 已提交
15615 15616 15617
/*
 * "quit" command
 */
15618
static const vshCmdInfo info_quit[] = {
15619
    {"help", N_("quit this interactive terminal")},
15620
    {"desc", ""},
15621
    {NULL, NULL}
K
Karel Zak 已提交
15622 15623
};

E
Eric Blake 已提交
15624
static bool
15625
cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
15626
{
E
Eric Blake 已提交
15627 15628
    ctl->imode = false;
    return true;
K
Karel Zak 已提交
15629 15630
}

15631 15632 15633 15634 15635 15636 15637
/* 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;
15638
    bool halt = false;
15639 15640 15641
    char *doc = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
E
Eric Blake 已提交
15642
    const char *name = NULL;
15643 15644

    snapshot = virDomainSnapshotCreateXML(dom, buffer, flags);
15645 15646 15647 15648 15649 15650 15651 15652 15653 15654 15655 15656 15657 15658 15659 15660 15661 15662 15663 15664 15665 15666 15667 15668

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

15669 15670 15671
    if (snapshot == NULL)
        goto cleanup;

15672 15673 15674 15675 15676
    if (halt && virDomainDestroy(dom) < 0) {
        virshReportError(ctl);
        goto cleanup;
    }

E
Eric Blake 已提交
15677
    name = virDomainSnapshotGetName(snapshot);
15678
    if (!name) {
E
Eric Blake 已提交
15679
        vshError(ctl, "%s", _("Could not get snapshot name"));
15680 15681 15682 15683 15684 15685 15686 15687 15688 15689 15690 15691 15692 15693 15694 15695 15696 15697 15698
        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;
}

15699 15700 15701 15702
/*
 * "snapshot-create" command
 */
static const vshCmdInfo info_snapshot_create[] = {
E
Eric Blake 已提交
15703 15704
    {"help", N_("Create a snapshot from XML")},
    {"desc", N_("Create a snapshot (disk and RAM) from XML")},
15705 15706 15707 15708 15709 15710
    {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")},
15711 15712 15713
    {"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")},
15714
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
15715
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
15716
    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse any existing external files")},
15717
    {"quiesce", VSH_OT_BOOL, 0, N_("quiesce guest's file systems")},
E
Eric Blake 已提交
15718
    {"atomic", VSH_OT_BOOL, 0, N_("require atomic operation")},
15719 15720 15721
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15722
static bool
15723 15724 15725
cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
15726
    bool ret = false;
15727
    const char *from = NULL;
15728
    char *buffer = NULL;
15729 15730 15731 15732 15733 15734 15735 15736
    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;
15737 15738
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
15739 15740
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
15741 15742
    if (vshCommandOptBool(cmd, "reuse-external"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT;
15743 15744
    if (vshCommandOptBool(cmd, "quiesce"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE;
E
Eric Blake 已提交
15745 15746
    if (vshCommandOptBool(cmd, "atomic"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC;
15747

15748
    if (!vshConnectionUsability(ctl, ctl->conn))
15749 15750 15751 15752 15753 15754
        goto cleanup;

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

15755
    if (vshCommandOptString(cmd, "xmlfile", &from) <= 0)
E
Eric Blake 已提交
15756
        buffer = vshStrdup(ctl, "<domainsnapshot/>");
15757 15758 15759 15760 15761 15762 15763 15764 15765 15766 15767 15768 15769 15770 15771
    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;
    }

15772
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, from);
15773 15774 15775 15776 15777 15778 15779 15780 15781

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

    return ret;
}

15782 15783 15784
/*
 * "snapshot-create-as" command
 */
15785 15786 15787 15788 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 15799 15800 15801 15802 15803 15804 15805 15806 15807 15808 15809 15810 15811 15812 15813 15814 15815 15816 15817 15818 15819 15820 15821 15822 15823 15824 15825 15826 15827 15828 15829 15830 15831 15832 15833 15834 15835 15836 15837 15838 15839 15840
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 */
            memmove(&tmp[1], &tmp[2], len - (tmp - spec) + 2);
            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;
}

15841 15842 15843 15844 15845 15846 15847 15848 15849 15850
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 已提交
15851
    {"print-xml", VSH_OT_BOOL, 0, N_("print XML document rather than create")},
15852
    {"no-metadata", VSH_OT_BOOL, 0, N_("take snapshot but create no metadata")},
15853
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
15854
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
15855
    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse any existing external files")},
15856
    {"quiesce", VSH_OT_BOOL, 0, N_("quiesce guest's file systems")},
E
Eric Blake 已提交
15857
    {"atomic", VSH_OT_BOOL, 0, N_("require atomic operation")},
15858 15859
    {"diskspec", VSH_OT_ARGV, 0,
     N_("disk attributes: disk[,snapshot=type][,driver=type][,file=name]")},
15860 15861 15862 15863 15864 15865 15866 15867 15868 15869 15870 15871
    {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;
15872
    unsigned int flags = 0;
15873
    const vshCmdOpt *opt = NULL;
15874 15875 15876

    if (vshCommandOptBool(cmd, "no-metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
15877 15878
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
15879 15880
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
15881 15882
    if (vshCommandOptBool(cmd, "reuse-external"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT;
15883 15884
    if (vshCommandOptBool(cmd, "quiesce"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE;
E
Eric Blake 已提交
15885 15886
    if (vshCommandOptBool(cmd, "atomic"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC;
15887 15888 15889 15890 15891 15892 15893 15894 15895 15896 15897 15898 15899 15900 15901 15902

    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)
15903
        virBufferEscapeString(&buf, "  <name>%s</name>\n", name);
15904
    if (desc)
15905
        virBufferEscapeString(&buf, "  <description>%s</description>\n", desc);
15906 15907 15908 15909 15910 15911 15912 15913 15914 15915
    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");
    }
15916 15917 15918 15919 15920 15921 15922 15923
    virBufferAddLit(&buf, "</domainsnapshot>\n");

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

E
Eric Blake 已提交
15924 15925 15926 15927 15928 15929
    if (vshCommandOptBool(cmd, "print-xml")) {
        vshPrint(ctl, "%s\n",  buffer);
        ret = true;
        goto cleanup;
    }

15930
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, NULL);
15931 15932 15933 15934 15935 15936 15937 15938 15939

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

    return ret;
}

15940 15941 15942 15943 15944 15945 15946 15947 15948 15949 15950 15951 15952 15953 15954 15955 15956 15957 15958 15959 15960 15961 15962 15963 15964 15965 15966 15967 15968 15969 15970 15971 15972 15973 15974 15975 15976 15977 15978
/* 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;
}

15979 15980 15981 15982 15983 15984 15985 15986 15987 15988 15989
/*
 * "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")},
15990
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
15991
    {"current", VSH_OT_BOOL, 0, N_("also set edited snapshot as current")},
15992 15993
    {"rename", VSH_OT_BOOL, 0, N_("allow renaming an existing snapshot")},
    {"clone", VSH_OT_BOOL, 0, N_("allow cloning to new name")},
15994 15995 15996 15997 15998 15999 16000 16001
    {NULL, 0, 0, NULL}
};

static bool
cmdSnapshotEdit(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    virDomainSnapshotPtr snapshot = NULL;
16002
    virDomainSnapshotPtr edited = NULL;
16003
    const char *name;
16004
    const char *edited_name;
16005 16006 16007 16008 16009 16010
    bool ret = false;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
    unsigned int define_flags = VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;
16011 16012 16013 16014 16015 16016 16017 16018
    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;
    }
16019

16020 16021
    if (vshCommandOptBool(cmd, "current") &&
        vshCommandOptBool(cmd, "snapshotname"))
16022 16023 16024 16025 16026 16027 16028 16029 16030
        define_flags |= VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;

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

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

16031 16032
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", false, dom,
                          &snapshot, &name) < 0)
16033 16034 16035 16036 16037 16038 16039
        goto cleanup;

    /* Get the XML configuration of the snapshot.  */
    doc = virDomainSnapshotGetXMLDesc(snapshot, getxml_flags);
    if (!doc)
        goto cleanup;

16040 16041 16042 16043
    /* strstr is safe here, since xml came from libvirt API and not user */
    if (strstr(doc, "<state>disk-snapshot</state>"))
        define_flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;

16044 16045 16046 16047 16048 16049 16050 16051 16052 16053 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068
    /* Create and open the temporary file.  */
    tmp = editWriteToTempFile(ctl, doc);
    if (!tmp)
        goto cleanup;

    /* Start the editor.  */
    if (editFile(ctl, tmp) == -1)
        goto cleanup;

    /* Read back the edited file.  */
    doc_edited = editReadBackFile(ctl, tmp);
    if (!doc_edited)
        goto cleanup;

    /* Compare original XML with edited.  Short-circuit if it did not
     * change, and we do not have any flags.  */
    if (STREQ(doc, doc_edited) &&
        !(define_flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT)) {
        vshPrint(ctl, _("Snapshot %s XML configuration not changed.\n"),
                 name);
        ret = true;
        goto cleanup;
    }

    /* Everything checks out, so redefine the xml.  */
16069 16070
    edited = virDomainSnapshotCreateXML(dom, doc_edited, define_flags);
    if (!edited) {
16071 16072 16073 16074
        vshError(ctl, _("Failed to update %s"), name);
        goto cleanup;
    }

16075 16076 16077 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094 16095 16096 16097 16098
    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;
        }
    }

16099 16100 16101 16102 16103 16104 16105 16106 16107 16108 16109
    ret = true;

cleanup:
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    if (tmp) {
        unlink(tmp);
        VIR_FREE(tmp);
    }
    if (snapshot)
        virDomainSnapshotFree(snapshot);
16110 16111
    if (edited)
        virDomainSnapshotFree(edited);
16112 16113 16114 16115 16116
    if (dom)
        virDomainFree(dom);
    return ret;
}

16117 16118 16119 16120
/*
 * "snapshot-current" command
 */
static const vshCmdInfo info_snapshot_current[] = {
16121 16122
    {"help", N_("Get or set the current snapshot")},
    {"desc", N_("Get or set the current snapshot")},
16123 16124 16125 16126 16127
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_current[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
16128
    {"name", VSH_OT_BOOL, 0, N_("list the name, rather than the full xml")},
16129 16130
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
16131 16132
    {"snapshotname", VSH_OT_DATA, 0,
     N_("name of existing snapshot to make current")},
16133 16134 16135
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
16136
static bool
16137 16138 16139
cmdSnapshotCurrent(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
16140
    bool ret = false;
16141 16142
    int current;
    virDomainSnapshotPtr snapshot = NULL;
16143
    char *xml = NULL;
16144
    const char *snapshotname = NULL;
16145
    unsigned int flags = 0;
16146
    const char *domname;
16147 16148 16149

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

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

16154
    dom = vshCommandOptDomain(ctl, cmd, &domname);
16155 16156 16157
    if (dom == NULL)
        goto cleanup;

16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177
    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;
16178 16179 16180
        /* 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;
16181 16182 16183 16184 16185 16186 16187 16188 16189
        snapshot2 = virDomainSnapshotCreateXML(dom, xml, flags);
        if (snapshot2 == NULL)
            goto cleanup;
        virDomainSnapshotFree(snapshot2);
        vshPrint(ctl, _("Snapshot %s set as current"), snapshotname);
        ret = true;
        goto cleanup;
    }

16190
    current = virDomainHasCurrentSnapshot(dom, 0);
16191 16192 16193 16194
    if (current < 0) {
        goto cleanup;
    } else if (!current) {
        vshError(ctl, _("domain '%s' has no current snapshot"), domname);
16195
        goto cleanup;
16196
    } else {
E
Eric Blake 已提交
16197
        const char *name = NULL;
16198 16199 16200 16201

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

16202
        if (vshCommandOptBool(cmd, "name")) {
E
Eric Blake 已提交
16203
            name = virDomainSnapshotGetName(snapshot);
16204 16205
            if (!name)
                goto cleanup;
E
Eric Blake 已提交
16206 16207 16208 16209
        } else {
            xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
            if (!xml)
                goto cleanup;
16210 16211 16212
        }

        vshPrint(ctl, "%s", name ? name : xml);
16213 16214
    }

E
Eric Blake 已提交
16215
    ret = true;
16216 16217

cleanup:
16218 16219
    if (!ret)
        virshReportError(ctl);
16220
    VIR_FREE(xml);
16221 16222 16223 16224 16225 16226 16227 16228
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

16229
/* Helper function to get the name of a snapshot's parent.  Caller
16230 16231 16232 16233 16234 16235
 * 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)
16236 16237 16238 16239 16240
{
    virDomainSnapshotPtr parent = NULL;
    char *xml = NULL;
    xmlDocPtr xmldoc = NULL;
    xmlXPathContextPtr ctxt = NULL;
16241 16242 16243
    int ret = -1;

    *parent_name = NULL;
16244 16245

    /* Try new API, since it is faster. */
16246
    if (!ctl->useSnapshotOld) {
16247 16248 16249
        parent = virDomainSnapshotGetParent(snapshot, 0);
        if (parent) {
            /* API works, and virDomainSnapshotGetName will succeed */
16250 16251
            *parent_name = vshStrdup(ctl, virDomainSnapshotGetName(parent));
            ret = 0;
16252 16253 16254 16255
            goto cleanup;
        }
        if (last_error->code == VIR_ERR_NO_DOMAIN_SNAPSHOT) {
            /* API works, and we found a root with no parent */
16256
            ret = 0;
16257 16258 16259
            goto cleanup;
        }
        /* API didn't work, fall back to XML scraping. */
16260
        ctl->useSnapshotOld = true;
16261 16262 16263 16264 16265 16266 16267 16268 16269 16270
    }

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

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

16271 16272
    *parent_name = virXPathString("string(/domainsnapshot/parent/name)", ctxt);
    ret = 0;
16273 16274

cleanup:
16275 16276 16277 16278 16279 16280 16281
    if (ret < 0) {
        virshReportError(ctl);
        vshError(ctl, "%s", _("unable to determine if snapshot has parent"));
    } else {
        virFreeError(last_error);
        last_error = NULL;
    }
16282 16283 16284 16285 16286
    if (parent)
        virDomainSnapshotFree(parent);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xmldoc);
    VIR_FREE(xml);
16287
    return ret;
16288 16289
}

16290 16291 16292 16293 16294 16295 16296 16297 16298 16299 16300
/*
 * "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")},
16301
    {"parent", VSH_OT_BOOL, 0, N_("add a column showing parent snapshot")},
16302
    {"roots", VSH_OT_BOOL, 0, N_("list only snapshots without parents")},
16303
    {"leaves", VSH_OT_BOOL, 0, N_("list only snapshots without children")},
16304 16305
    {"metadata", VSH_OT_BOOL, 0,
     N_("list only snapshots that have metadata that would prevent undefine")},
16306
    {"tree", VSH_OT_BOOL, 0, N_("list snapshots in a tree")},
16307
    {"from", VSH_OT_DATA, 0, N_("limit list to children of given snapshot")},
16308 16309
    {"current", VSH_OT_BOOL, 0,
     N_("limit list to children of current snapshot")},
16310
    {"descendants", VSH_OT_BOOL, 0, N_("with --from, list all descendants")},
16311 16312 16313
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
16314
static bool
16315 16316 16317
cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
16318
    bool ret = false;
16319
    unsigned int flags = 0;
16320 16321
    int parent_filter = 0; /* -1 for roots filtering, 0 for no parent
                              information needed, 1 for parent column */
16322 16323
    int numsnaps;
    char **names = NULL;
16324
    char **parents = NULL;
16325
    int actual = 0;
16326 16327 16328 16329 16330 16331
    int i;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    char *doc = NULL;
    virDomainSnapshotPtr snapshot = NULL;
    char *state = NULL;
16332
    char *parent = NULL;
16333 16334
    long long creation_longlong;
    time_t creation_time_t;
16335 16336
    char timestr[100];
    struct tm time_info;
16337
    bool tree = vshCommandOptBool(cmd, "tree");
16338
    bool leaves = vshCommandOptBool(cmd, "leaves");
16339 16340
    const char *from = NULL;
    virDomainSnapshotPtr start = NULL;
16341
    int start_index = -1;
16342
    bool descendants = false;
16343

16344 16345 16346 16347 16348 16349 16350 16351 16352 16353
    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)
16354
        goto cleanup;
16355

16356
    if (vshCommandOptBool(cmd, "parent")) {
16357 16358
        if (vshCommandOptBool(cmd, "roots")) {
            vshError(ctl, "%s",
16359
                     _("--parent and --roots are mutually exclusive"));
16360 16361
            return false;
        }
16362 16363
        if (tree) {
            vshError(ctl, "%s",
16364
                     _("--parent and --tree are mutually exclusive"));
16365 16366
            return false;
        }
16367
        parent_filter = 1;
16368
    } else if (vshCommandOptBool(cmd, "roots")) {
16369 16370
        if (tree) {
            vshError(ctl, "%s",
16371
                     _("--roots and --tree are mutually exclusive"));
16372 16373
            return false;
        }
16374 16375 16376 16377
        if (from) {
            vshError(ctl, "%s",
                     _("--roots and --from are mutually exclusive"));
        }
16378 16379
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
    }
16380 16381 16382 16383 16384 16385 16386 16387
    if (leaves) {
        if (tree) {
            vshError(ctl, "%s",
                     _("--leaves and --tree are mutually exclusive"));
            return false;
        }
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_LEAVES;
    }
16388 16389 16390

    if (vshCommandOptBool(cmd, "metadata")) {
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_METADATA;
16391 16392
    }

16393
    if (from) {
16394 16395
        descendants = vshCommandOptBool(cmd, "descendants");
        if (descendants || tree) {
16396 16397
            flags |= VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
        }
16398 16399
        numsnaps = ctl->useSnapshotOld ? -1 :
            virDomainSnapshotNumChildren(start, flags);
16400
        if (numsnaps < 0) {
16401 16402
            if (ctl->useSnapshotOld ||
                last_error->code == VIR_ERR_NO_SUPPORT) {
16403
                /* We can emulate --from.  */
16404
                /* XXX can we also emulate --leaves? */
16405 16406 16407 16408 16409 16410
                virFreeError(last_error);
                last_error = NULL;
                ctl->useSnapshotOld = true;
                flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
                numsnaps = virDomainSnapshotNum(dom, flags);
            }
16411 16412
        } else if (tree) {
            numsnaps++;
16413
        }
16414
    } else {
16415
        numsnaps = virDomainSnapshotNum(dom, flags);
16416 16417

        /* Fall back to simulation if --roots was unsupported. */
16418
        /* XXX can we also emulate --leaves? */
16419 16420 16421 16422 16423 16424 16425 16426
        if (numsnaps < 0 && last_error->code == VIR_ERR_INVALID_ARG &&
            (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS)) {
            virFreeError(last_error);
            last_error = NULL;
            parent_filter = -1;
            flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
            numsnaps = virDomainSnapshotNum(dom, flags);
        }
16427 16428
    }

16429 16430 16431
    if (numsnaps < 0) {
        if (!last_error)
            vshError(ctl, _("missing support"));
16432
        goto cleanup;
16433
    }
16434

16435 16436 16437 16438 16439 16440 16441 16442 16443
    if (!tree) {
        if (parent_filter > 0)
            vshPrintExtra(ctl, " %-20s %-25s %-15s %s",
                          _("Name"), _("Creation Time"), _("State"),
                          _("Parent"));
        else
            vshPrintExtra(ctl, " %-20s %-25s %s",
                          _("Name"), _("Creation Time"), _("State"));
        vshPrintExtra(ctl, "\n\
16444
------------------------------------------------------------\n");
16445
    }
16446

16447 16448 16449 16450
    if (!numsnaps) {
        ret = true;
        goto cleanup;
    }
16451

16452 16453 16454
    if (VIR_ALLOC_N(names, numsnaps) < 0)
        goto cleanup;

16455
    if (from && !ctl->useSnapshotOld) {
16456 16457 16458 16459 16460 16461 16462 16463 16464 16465 16466 16467 16468 16469 16470 16471 16472 16473
        /* When mixing --from and --tree, we want to start the tree at the
         * given snapshot.  Without --tree, only list the children.  */
        if (tree) {
            if (numsnaps)
                actual = virDomainSnapshotListChildrenNames(start, names + 1,
                                                            numsnaps - 1,
                                                            flags);
            if (actual >= 0) {
                actual++;
                names[0] = vshStrdup(ctl, from);
            }
        } else {
            actual = virDomainSnapshotListChildrenNames(start, names,
                                                        numsnaps, flags);
        }
    } else {
        actual = virDomainSnapshotListNames(dom, names, numsnaps, flags);
    }
16474 16475
    if (actual < 0)
        goto cleanup;
16476

E
Eric Blake 已提交
16477 16478
    qsort(&names[0], actual, sizeof(char*), namesorter);

16479 16480
    if (tree || ctl->useSnapshotOld) {
        parents = vshCalloc(ctl, sizeof(char *), actual);
16481
        for (i = (from && !ctl->useSnapshotOld); i < actual; i++) {
16482 16483 16484 16485 16486
            if (ctl->useSnapshotOld && STREQ(names[i], from)) {
                start_index = i;
                continue;
            }

16487 16488 16489 16490
            /* free up memory from previous iterations of the loop */
            if (snapshot)
                virDomainSnapshotFree(snapshot);
            snapshot = virDomainSnapshotLookupByName(dom, names[i], 0);
16491 16492
            if (!snapshot ||
                vshGetSnapshotParent(ctl, snapshot, &parents[i]) < 0) {
16493 16494 16495 16496 16497 16498
                while (--i >= 0)
                    VIR_FREE(parents[i]);
                VIR_FREE(parents);
                goto cleanup;
            }
        }
16499 16500 16501
    }
    if (tree) {
        char indentBuf[INDENT_BUFLEN];
16502 16503
        for (i = 0 ; i < actual ; i++) {
            memset(indentBuf, '\0', sizeof indentBuf);
16504
            if (ctl->useSnapshotOld ? STREQ(names[i], from) : !parents[i])
16505 16506 16507 16508 16509 16510 16511 16512 16513 16514 16515 16516 16517 16518
                cmdNodeListDevicesPrint(ctl,
                                        names,
                                        parents,
                                        actual,
                                        i,
                                        i,
                                        0,
                                        0,
                                        indentBuf);
        }

        ret = true;
        goto cleanup;
    } else {
16519
        if (ctl->useSnapshotOld && descendants) {
16520 16521 16522 16523 16524 16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 16548 16549 16550 16551 16552 16553 16554 16555 16556 16557 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569
            bool changed = false;

            /* Make multiple passes over the list - first pass NULLs
             * out all roots except start, remaining passes NULL out
             * any entry whose parent is not still in list.  Also, we
             * NULL out parent when name is known to be in list.
             * Sorry, this is O(n^3) - hope your hierarchy isn't huge.  */
            if (start_index < 0) {
                vshError(ctl, _("snapshot %s disappeared from list"), from);
                goto cleanup;
            }
            for (i = 0; i < actual; i++) {
                if (i == start_index)
                    continue;
                if (!parents[i]) {
                    VIR_FREE(names[i]);
                } else if (STREQ(parents[i], from)) {
                    VIR_FREE(parents[i]);
                    changed = true;
                }
            }
            if (!changed) {
                ret = true;
                goto cleanup;
            }
            while (changed) {
                changed = false;
                for (i = 0; i < actual; i++) {
                    bool found = false;
                    int j;

                    if (!names[i] || !parents[i])
                        continue;
                    for (j = 0; j < actual; j++) {
                        if (!names[j] || i == j)
                            continue;
                        if (STREQ(parents[i], names[j])) {
                            found = true;
                            if (!parents[j])
                                VIR_FREE(parents[i]);
                            break;
                        }
                    }
                    if (!found) {
                        changed = true;
                        VIR_FREE(names[i]);
                    }
                }
            }
            VIR_FREE(names[start_index]);
16570 16571
        }

16572
        for (i = 0; i < actual; i++) {
16573 16574
            if (ctl->useSnapshotOld &&
                (descendants ? !names[i] : STRNEQ_NULLABLE(parents[i], from)))
16575 16576
                continue;

16577
            /* free up memory from previous iterations of the loop */
16578
            VIR_FREE(parent);
16579 16580 16581 16582
            VIR_FREE(state);
            if (snapshot)
                virDomainSnapshotFree(snapshot);
            xmlXPathFreeContext(ctxt);
16583
            xmlFreeDoc(xml);
16584 16585 16586 16587 16588 16589 16590 16591 16592 16593
            VIR_FREE(doc);

            snapshot = virDomainSnapshotLookupByName(dom, names[i], 0);
            if (snapshot == NULL)
                continue;

            doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
            if (!doc)
                continue;

16594
            xml = virXMLParseStringCtxt(doc, _("(domain_snapshot)"), &ctxt);
16595 16596 16597
            if (!xml)
                continue;

16598 16599 16600
            if (parent_filter) {
                parent = virXPathString("string(/domainsnapshot/parent/name)",
                                        ctxt);
16601 16602
                if (!parent && parent_filter < 0)
                    continue;
16603 16604
            }

16605 16606 16607
            state = virXPathString("string(/domainsnapshot/state)", ctxt);
            if (state == NULL)
                continue;
16608 16609
            if (virXPathLongLong("string(/domainsnapshot/creationTime)", ctxt,
                                 &creation_longlong) < 0)
16610
                continue;
16611 16612 16613 16614 16615 16616
            creation_time_t = creation_longlong;
            if (creation_time_t != creation_longlong) {
                vshError(ctl, "%s", _("time_t overflow"));
                continue;
            }
            localtime_r(&creation_time_t, &time_info);
16617 16618
            strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S %z",
                     &time_info);
16619

16620 16621 16622 16623 16624
            if (parent)
                vshPrint(ctl, " %-20s %-25s %-15s %s\n",
                         names[i], timestr, state, parent);
            else
                vshPrint(ctl, " %-20s %-25s %s\n", names[i], timestr, state);
16625 16626 16627
        }
    }

E
Eric Blake 已提交
16628
    ret = true;
16629 16630 16631

cleanup:
    /* this frees up memory from the last iteration of the loop */
16632
    VIR_FREE(parent);
16633 16634 16635
    VIR_FREE(state);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
16636 16637
    if (start)
        virDomainSnapshotFree(start);
16638
    xmlXPathFreeContext(ctxt);
16639
    xmlFreeDoc(xml);
16640
    VIR_FREE(doc);
16641
    for (i = 0; i < actual; i++) {
16642
        VIR_FREE(names[i]);
16643 16644 16645
        if (parents)
            VIR_FREE(parents[i]);
    }
16646
    VIR_FREE(names);
16647
    VIR_FREE(parents);
16648 16649 16650 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665
    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")},
16666 16667
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
16668 16669 16670
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
16671
static bool
16672 16673 16674
cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
16675
    bool ret = false;
16676
    const char *name = NULL;
16677 16678
    virDomainSnapshotPtr snapshot = NULL;
    char *xml = NULL;
16679 16680 16681 16682
    unsigned int flags = 0;

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

16684
    if (!vshConnectionUsability(ctl, ctl->conn))
16685 16686 16687 16688 16689 16690
        goto cleanup;

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

16691
    if (vshCommandOptString(cmd, "snapshotname", &name) <= 0)
16692 16693 16694 16695 16696 16697
        goto cleanup;

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

16698
    xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
16699 16700 16701
    if (!xml)
        goto cleanup;

16702
    vshPrint(ctl, "%s", xml);
16703

E
Eric Blake 已提交
16704
    ret = true;
16705 16706 16707 16708 16709 16710 16711 16712 16713 16714 16715

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

    return ret;
}

E
Eric Blake 已提交
16716 16717 16718 16719
/*
 * "snapshot-parent" command
 */
static const vshCmdInfo info_snapshot_parent[] = {
E
Eric Blake 已提交
16720
    {"help", N_("Get the name of the parent of a snapshot")},
E
Eric Blake 已提交
16721 16722 16723 16724 16725 16726
    {"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")},
16727 16728
    {"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 已提交
16729 16730 16731 16732 16733 16734 16735 16736 16737 16738 16739 16740 16741 16742 16743 16744 16745 16746 16747
    {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;

16748 16749
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
E
Eric Blake 已提交
16750 16751
        goto cleanup;

16752
    if (vshGetSnapshotParent(ctl, snapshot, &parent) < 0)
E
Eric Blake 已提交
16753
        goto cleanup;
16754 16755 16756 16757
    if (!parent) {
        vshError(ctl, _("snapshot '%s' has no parent"), name);
        goto cleanup;
    }
E
Eric Blake 已提交
16758 16759 16760 16761 16762 16763 16764 16765 16766 16767 16768 16769 16770 16771 16772

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

    ret = true;

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

    return ret;
}

16773
/*
16774
 * "snapshot-revert" command
16775
 */
16776
static const vshCmdInfo info_snapshot_revert[] = {
16777 16778 16779 16780 16781
    {"help", N_("Revert a domain to a snapshot")},
    {"desc", N_("Revert domain to snapshot")},
    {NULL, NULL}
};

16782
static const vshCmdOptDef opts_snapshot_revert[] = {
16783
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
16784 16785
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("revert to current snapshot")},
16786 16787
    {"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 已提交
16788
    {"force", VSH_OT_BOOL, 0, N_("try harder on risky reverts")},
16789 16790 16791
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
16792
static bool
16793
cmdDomainSnapshotRevert(vshControl *ctl, const vshCmd *cmd)
16794 16795
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
16796
    bool ret = false;
16797
    const char *name = NULL;
16798
    virDomainSnapshotPtr snapshot = NULL;
16799
    unsigned int flags = 0;
E
Eric Blake 已提交
16800 16801
    bool force = false;
    int result;
16802 16803 16804 16805 16806

    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED;
E
Eric Blake 已提交
16807 16808 16809 16810 16811 16812
    /* 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;
16813

16814
    if (!vshConnectionUsability(ctl, ctl->conn))
16815 16816 16817 16818 16819 16820
        goto cleanup;

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

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

E
Eric Blake 已提交
16825 16826 16827 16828 16829 16830 16831 16832 16833
    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)
16834 16835
        goto cleanup;

E
Eric Blake 已提交
16836
    ret = true;
16837 16838 16839 16840 16841 16842 16843 16844 16845 16846 16847 16848 16849 16850 16851 16852 16853 16854 16855 16856 16857

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")},
16858 16859
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("delete current snapshot")},
16860
    {"children", VSH_OT_BOOL, 0, N_("delete snapshot and all children")},
16861 16862 16863
    {"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")},
16864 16865 16866
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
16867
static bool
16868 16869 16870
cmdSnapshotDelete(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
16871
    bool ret = false;
16872
    const char *name = NULL;
16873 16874 16875
    virDomainSnapshotPtr snapshot = NULL;
    unsigned int flags = 0;

16876
    if (!vshConnectionUsability(ctl, ctl->conn))
16877 16878 16879 16880 16881 16882
        goto cleanup;

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

16883 16884
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
16885 16886 16887 16888
        goto cleanup;

    if (vshCommandOptBool(cmd, "children"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN;
16889 16890 16891 16892
    if (vshCommandOptBool(cmd, "children-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY;
    if (vshCommandOptBool(cmd, "metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY;
16893

16894 16895 16896
    /* 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.  */
16897
    if (virDomainSnapshotDelete(snapshot, flags) == 0) {
16898 16899 16900 16901
        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);
16902 16903
    } else {
        vshError(ctl, _("Failed to delete snapshot %s"), name);
16904
        goto cleanup;
16905
    }
16906

E
Eric Blake 已提交
16907
    ret = true;
16908 16909 16910 16911 16912 16913 16914 16915 16916 16917

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

    return ret;
}

16918 16919 16920 16921 16922 16923 16924 16925 16926 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 16941 16942 16943 16944 16945 16946 16947 16948 16949 16950 16951 16952 16953 16954 16955 16956 16957 16958 16959 16960 16961 16962 16963 16964 16965 16966 16967 16968 16969 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994
/*
 * "domblkerror" command
 */
static const char *
vshDomainIOErrorToString(int error)
{
    switch ((virDomainDiskErrorCode) error) {
    case VIR_DOMAIN_DISK_ERROR_NONE:
        return _("no error");
    case VIR_DOMAIN_DISK_ERROR_UNSPEC:
        return _("unspecified error");
    case VIR_DOMAIN_DISK_ERROR_NO_SPACE:
        return _("no space");
    case VIR_DOMAIN_DISK_ERROR_LAST:
        ;
    }

    return _("unknown error");
}

static const vshCmdInfo info_domblkerror[] = {
    {"help", N_("Show errors on block devices")},
    {"desc", N_("Show block device errors")},
    {NULL, NULL}
};

static const vshCmdOptDef opts_domblkerror[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id, or uuid")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomBlkError(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
    virDomainDiskErrorPtr disks = NULL;
    unsigned int ndisks;
    int i;
    int count;
    bool ret = false;

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

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

    if ((count = virDomainGetDiskErrors(dom, NULL, 0, 0)) < 0)
        goto cleanup;
    ndisks = count;

    if (ndisks) {
        if (VIR_ALLOC_N(disks, ndisks) < 0)
            goto cleanup;

        if ((count = virDomainGetDiskErrors(dom, disks, ndisks, 0)) == -1)
            goto cleanup;
    }

    if (count == 0) {
        vshPrint(ctl, _("No errors found\n"));
    } else {
        for (i = 0; i < count; i++) {
            vshPrint(ctl, "%s: %s\n",
                     disks[i].disk,
                     vshDomainIOErrorToString(disks[i].error));
        }
    }

    ret = true;

cleanup:
    VIR_FREE(disks);
    virDomainFree(dom);
    return ret;
}

16995 16996 16997 16998
/*
 * "qemu-monitor-command" command
 */
static const vshCmdInfo info_qemu_monitor_command[] = {
16999 17000
    {"help", N_("QEMU Monitor Command")},
    {"desc", N_("QEMU Monitor Command")},
17001 17002 17003 17004 17005
    {NULL, NULL}
};

static const vshCmdOptDef opts_qemu_monitor_command[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
17006
    {"hmp", VSH_OT_BOOL, 0, N_("command is in human monitor protocol")},
17007
    {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")},
17008 17009 17010
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
17011
static bool
17012 17013 17014
cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
17015
    bool ret = false;
17016
    char *monitor_cmd = NULL;
17017
    char *result = NULL;
17018
    unsigned int flags = 0;
17019 17020 17021
    const vshCmdOpt *opt = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    bool pad = false;
17022 17023 17024 17025 17026 17027 17028 17029

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

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

17030 17031 17032 17033 17034 17035 17036 17037
    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"));
17038 17039
        goto cleanup;
    }
17040
    monitor_cmd = virBufferContentAndReset(&buf);
17041

17042 17043 17044 17045
    if (vshCommandOptBool(cmd, "hmp"))
        flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP;

    if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0)
17046 17047 17048 17049
        goto cleanup;

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

E
Eric Blake 已提交
17050
    ret = true;
17051 17052 17053

cleanup:
    VIR_FREE(result);
17054
    VIR_FREE(monitor_cmd);
17055 17056 17057 17058 17059 17060
    if (dom)
        virDomainFree(dom);

    return ret;
}

17061 17062 17063 17064 17065 17066 17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 17080
/*
 * "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;
17081
    unsigned int pid_value; /* API uses unsigned int, not pid_t */
17082 17083 17084 17085

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

17086
    if (vshCommandOptUInt(cmd, "pid", &pid_value) <= 0) {
17087 17088 17089 17090
        vshError(ctl, "%s", _("missing pid value"));
        goto cleanup;
    }

17091
    if (!(dom = virDomainQemuAttach(ctl->conn, pid_value, flags)))
17092 17093 17094 17095
        goto cleanup;

    if (dom != NULL) {
        vshPrint(ctl, _("Domain %s attached to pid %u\n"),
17096
                 virDomainGetName(dom), pid_value);
17097 17098 17099
        virDomainFree(dom);
        ret = true;
    } else {
17100
        vshError(ctl, _("Failed to attach to pid %u"), pid_value);
17101 17102 17103 17104 17105 17106
    }

cleanup:
    return ret;
}

17107
static const vshCmdDef domManagementCmds[] = {
17108 17109 17110 17111 17112 17113 17114
    {"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},
L
Lei Li 已提交
17115
    {"blkdeviotune", cmdBlkdeviotune, opts_blkdeviotune, info_blkdeviotune, 0},
17116
    {"blkiotune", cmdBlkiotune, opts_blkiotune, info_blkiotune, 0},
17117 17118
    {"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0},
    {"blockjob", cmdBlockJob, opts_block_job, info_block_job, 0},
17119
    {"blockresize", cmdBlockResize, opts_block_resize, info_block_resize, 0},
O
Osier Yang 已提交
17120
    {"change-media", cmdChangeMedia, opts_change_media, info_change_media, 0},
17121
#ifndef WIN32
17122
    {"console", cmdConsole, opts_console, info_console, 0},
17123
#endif
17124 17125
    {"cpu-baseline", cmdCPUBaseline, opts_cpu_baseline, info_cpu_baseline, 0},
    {"cpu-compare", cmdCPUCompare, opts_cpu_compare, info_cpu_compare, 0},
17126
    {"cpu-stats", cmdCPUStats, opts_cpu_stats, info_cpu_stats, 0},
17127 17128
    {"create", cmdCreate, opts_create, info_create, 0},
    {"define", cmdDefine, opts_define, info_define, 0},
17129
    {"desc", cmdDesc, opts_desc, info_desc, 0},
17130 17131 17132 17133 17134 17135 17136
    {"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},
    {"domid", cmdDomid, opts_domid, info_domid, 0},
17137
    {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink, 0},
17138
    {"domiftune", cmdDomIftune, opts_domiftune, info_domiftune, 0},
17139 17140 17141
    {"domjobabort", cmdDomjobabort, opts_domjobabort, info_domjobabort, 0},
    {"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo, 0},
    {"domname", cmdDomname, opts_domname, info_domname, 0},
17142 17143
    {"dompmsuspend", cmdDomPMSuspend,
     opts_dom_pm_suspend, info_dom_pm_suspend, 0},
17144 17145
    {"dompmwakeup", cmdDomPMWakeup,
     opts_dom_pm_wakeup, info_dom_pm_wakeup, 0},
17146 17147 17148 17149 17150 17151 17152 17153 17154
    {"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},
17155
    {"send-key", cmdSendKey, opts_send_key, info_send_key, 0},
17156 17157 17158 17159 17160 17161 17162 17163
    {"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},
17164 17165
    {"migrate-setspeed", cmdMigrateSetMaxSpeed,
     opts_migrate_setspeed, info_migrate_setspeed, 0},
17166 17167
    {"migrate-getspeed", cmdMigrateGetMaxSpeed,
     opts_migrate_getspeed, info_migrate_getspeed, 0},
H
Hu Tao 已提交
17168
    {"numatune", cmdNumatune, opts_numatune, info_numatune, 0},
17169
    {"reboot", cmdReboot, opts_reboot, info_reboot, 0},
X
Xu He Jie 已提交
17170
    {"reset", cmdReset, opts_reset, info_reset, 0},
17171 17172 17173
    {"restore", cmdRestore, opts_restore, info_restore, 0},
    {"resume", cmdResume, opts_resume, info_resume, 0},
    {"save", cmdSave, opts_save, info_save, 0},
17174 17175 17176 17177 17178 17179
    {"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},
17180
    {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo, 0},
17181
    {"screenshot", cmdScreenshot, opts_screenshot, info_screenshot, 0},
17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193
    {"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},
E
Eric Blake 已提交
17194
    {"vcpupin", cmdVcpuPin, opts_vcpupin, info_vcpupin, 0},
17195 17196
    {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay, 0},
    {NULL, NULL, NULL, NULL, 0}
17197 17198 17199
};

static const vshCmdDef domMonitoringCmds[] = {
17200
    {"domblkerror", cmdDomBlkError, opts_domblkerror, info_domblkerror, 0},
17201
    {"domblkinfo", cmdDomblkinfo, opts_domblkinfo, info_domblkinfo, 0},
17202
    {"domblklist", cmdDomblklist, opts_domblklist, info_domblklist, 0},
17203
    {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat, 0},
17204
    {"domcontrol", cmdDomControl, opts_domcontrol, info_domcontrol, 0},
17205
    {"domif-getlink", cmdDomIfGetLink, opts_domif_getlink, info_domif_getlink, 0},
17206
    {"domiflist", cmdDomiflist, opts_domiflist, info_domiflist, 0},
17207 17208 17209 17210 17211 17212
    {"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}
17213 17214 17215
};

static const vshCmdDef storagePoolCmds[] = {
17216
    {"find-storage-pool-sources-as", cmdPoolDiscoverSourcesAs,
17217
     opts_find_storage_pool_sources_as, info_find_storage_pool_sources_as, 0},
17218
    {"find-storage-pool-sources", cmdPoolDiscoverSources,
17219 17220 17221 17222 17223 17224 17225 17226 17227 17228 17229 17230 17231 17232 17233 17234 17235 17236 17237 17238 17239
     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}
17240 17241 17242
};

static const vshCmdDef storageVolCmds[] = {
17243 17244 17245 17246 17247 17248 17249 17250 17251 17252 17253 17254 17255 17256 17257
    {"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},
17258
    {"vol-resize", cmdVolResize, opts_vol_resize, info_vol_resize, 0},
17259 17260 17261
    {"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}
17262
};
17263

17264
static const vshCmdDef networkCmds[] = {
17265 17266 17267 17268 17269 17270 17271 17272 17273 17274 17275 17276 17277 17278 17279 17280 17281 17282 17283
    {"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}
17284
};
17285

17286
static const vshCmdDef nodedevCmds[] = {
17287 17288 17289 17290
    {"nodedev-create", cmdNodeDeviceCreate, opts_node_device_create,
     info_node_device_create, 0},
    {"nodedev-destroy", cmdNodeDeviceDestroy, opts_node_device_destroy,
     info_node_device_destroy, 0},
17291 17292 17293 17294
    {"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},
17295 17296 17297 17298 17299 17300 17301 17302 17303
    {"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}
17304 17305 17306
};

static const vshCmdDef ifaceCmds[] = {
E
Eric Blake 已提交
17307 17308
    {"iface-begin", cmdInterfaceBegin, opts_interface_begin,
     info_interface_begin, 0},
17309 17310
    {"iface-bridge", cmdInterfaceBridge, opts_interface_bridge,
     info_interface_bridge, 0},
E
Eric Blake 已提交
17311 17312
    {"iface-commit", cmdInterfaceCommit, opts_interface_commit,
     info_interface_commit, 0},
17313 17314 17315 17316 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326
    {"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},
E
Eric Blake 已提交
17327 17328
    {"iface-rollback", cmdInterfaceRollback, opts_interface_rollback,
     info_interface_rollback, 0},
17329 17330
    {"iface-start", cmdInterfaceStart, opts_interface_start,
     info_interface_start, 0},
17331 17332
    {"iface-unbridge", cmdInterfaceUnbridge, opts_interface_unbridge,
     info_interface_unbridge, 0},
17333 17334 17335
    {"iface-undefine", cmdInterfaceUndefine, opts_interface_undefine,
     info_interface_undefine, 0},
    {NULL, NULL, NULL, NULL, 0}
17336
};
17337

17338
static const vshCmdDef nwfilterCmds[] = {
17339 17340 17341 17342 17343 17344 17345 17346 17347 17348 17349
    {"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}
17350
};
17351

17352
static const vshCmdDef secretCmds[] = {
17353 17354 17355 17356 17357 17358 17359 17360 17361 17362 17363 17364
    {"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}
17365
};
17366

17367
static const vshCmdDef virshCmds[] = {
17368 17369 17370 17371 17372 17373 17374
    {"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}
17375
};
17376

17377
static const vshCmdDef snapshotCmds[] = {
17378 17379
    {"snapshot-create", cmdSnapshotCreate, opts_snapshot_create,
     info_snapshot_create, 0},
17380 17381
    {"snapshot-create-as", cmdSnapshotCreateAs, opts_snapshot_create_as,
     info_snapshot_create_as, 0},
17382 17383 17384 17385 17386 17387
    {"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},
17388 17389
    {"snapshot-edit", cmdSnapshotEdit, opts_snapshot_edit,
     info_snapshot_edit, 0},
17390 17391
    {"snapshot-list", cmdSnapshotList, opts_snapshot_list,
     info_snapshot_list, 0},
E
Eric Blake 已提交
17392 17393
    {"snapshot-parent", cmdSnapshotParent, opts_snapshot_parent,
     info_snapshot_parent, 0},
17394 17395 17396
    {"snapshot-revert", cmdDomainSnapshotRevert, opts_snapshot_revert,
     info_snapshot_revert, 0},
    {NULL, NULL, NULL, NULL, 0}
17397
};
17398

17399
static const vshCmdDef hostAndHypervisorCmds[] = {
17400 17401 17402 17403 17404
    {"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},
17405
    {"nodecpustats", cmdNodeCpuStats, opts_node_cpustats, info_nodecpustats, 0},
17406
    {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo, 0},
17407
    {"nodememstats", cmdNodeMemStats, opts_node_memstats, info_nodememstats, 0},
17408
    {"nodesuspend", cmdNodeSuspend, opts_node_suspend, info_nodesuspend, 0},
17409
    {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0},
17410 17411 17412 17413
    {"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},
17414
    {"version", cmdVersion, opts_version, info_version, 0},
17415
    {NULL, NULL, NULL, NULL, 0}
K
Karel Zak 已提交
17416 17417
};

17418 17419 17420 17421 17422 17423 17424 17425 17426 17427 17428 17429 17430 17431 17432 17433 17434
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 已提交
17435 17436 17437 17438
/* ---------------
 * Utils for work with command definition
 * ---------------
 */
K
Karel Zak 已提交
17439
static const char *
17440
vshCmddefGetInfo(const vshCmdDef * cmd, const char *name)
17441
{
17442
    const vshCmdInfo *info;
17443

K
Karel Zak 已提交
17444
    for (info = cmd->info; info && info->name; info++) {
17445
        if (STREQ(info->name, name))
K
Karel Zak 已提交
17446 17447 17448 17449 17450
            return info->data;
    }
    return NULL;
}

17451
/* Validate that the options associated with cmd can be parsed.  */
17452
static int
E
Eric Blake 已提交
17453
vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg,
17454 17455 17456 17457 17458
                  uint32_t *opts_required)
{
    int i;
    bool optional = false;

E
Eric Blake 已提交
17459 17460 17461
    *opts_need_arg = 0;
    *opts_required = 0;

17462 17463 17464 17465 17466 17467 17468 17469 17470
    if (!cmd->opts)
        return 0;

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

        if (i > 31)
            return -1; /* too many options */
        if (opt->type == VSH_OT_BOOL) {
E
Eric Blake 已提交
17471
            if (opt->flags & VSH_OFLAG_REQ)
17472 17473 17474
                return -1; /* bool options can't be mandatory */
            continue;
        }
E
Eric Blake 已提交
17475 17476 17477 17478 17479 17480 17481 17482 17483 17484 17485 17486
        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 已提交
17487 17488
        if (opt->flags & VSH_OFLAG_REQ_OPT) {
            if (opt->flags & VSH_OFLAG_REQ)
L
Lai Jiangshan 已提交
17489 17490 17491 17492
                *opts_required |= 1 << i;
            continue;
        }

17493
        *opts_need_arg |= 1 << i;
E
Eric Blake 已提交
17494
        if (opt->flags & VSH_OFLAG_REQ) {
17495 17496 17497 17498 17499 17500
            if (optional)
                return -1; /* mandatory options must be listed first */
            *opts_required |= 1 << i;
        } else {
            optional = true;
        }
17501 17502 17503

        if (opt->type == VSH_OT_ARGV && cmd->opts[i + 1].name)
            return -1; /* argv option must be listed last */
17504 17505 17506 17507
    }
    return 0;
}

17508
static const vshCmdOptDef *
17509
vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
17510
                   uint32_t *opts_seen, int *opt_index)
17511
{
17512 17513 17514 17515
    int i;

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

17517
        if (STREQ(opt->name, name)) {
E
Eric Blake 已提交
17518 17519 17520 17521
            if (opt->type == VSH_OT_ALIAS) {
                name = opt->help;
                continue;
            }
17522
            if ((*opts_seen & (1 << i)) && opt->type != VSH_OT_ARGV) {
17523 17524 17525
                vshError(ctl, _("option --%s already seen"), name);
                return NULL;
            }
17526 17527
            *opts_seen |= 1 << i;
            *opt_index = i;
K
Karel Zak 已提交
17528
            return opt;
17529 17530 17531 17532 17533
        }
    }

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

17537
static const vshCmdOptDef *
17538 17539
vshCmddefGetData(const vshCmdDef *cmd, uint32_t *opts_need_arg,
                 uint32_t *opts_seen)
17540
{
17541
    int i;
17542
    const vshCmdOptDef *opt;
K
Karel Zak 已提交
17543

17544 17545 17546 17547
    if (!*opts_need_arg)
        return NULL;

    /* Grab least-significant set bit */
E
Eric Blake 已提交
17548
    i = ffs(*opts_need_arg) - 1;
17549
    opt = &cmd->opts[i];
17550
    if (opt->type != VSH_OT_ARGV)
17551
        *opts_need_arg &= ~(1 << i);
17552
    *opts_seen |= 1 << i;
17553
    return opt;
K
Karel Zak 已提交
17554 17555
}

17556 17557 17558
/*
 * Checks for required options
 */
17559
static int
17560 17561
vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required,
                    uint32_t opts_seen)
17562
{
17563
    const vshCmdDef *def = cmd->def;
17564 17565 17566 17567 17568 17569 17570 17571 17572
    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];
17573

17574
            vshError(ctl,
17575
                     opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ?
17576 17577 17578
                     _("command '%s' requires <%s> option") :
                     _("command '%s' requires --%s option"),
                     def->name, opt->name);
17579 17580
        }
    }
17581
    return -1;
17582 17583
}

17584
static const vshCmdDef *
17585 17586
vshCmddefSearch(const char *cmdname)
{
17587
    const vshCmdGrp *g;
17588
    const vshCmdDef *c;
17589

17590 17591
    for (g = cmdGroups; g->name; g++) {
        for (c = g->commands; c->name; c++) {
17592
            if (STREQ(c->name, cmdname))
17593 17594 17595 17596
                return c;
        }
    }

K
Karel Zak 已提交
17597 17598 17599
    return NULL;
}

17600 17601 17602 17603 17604 17605
static const vshCmdGrp *
vshCmdGrpSearch(const char *grpname)
{
    const vshCmdGrp *g;

    for (g = cmdGroups; g->name; g++) {
17606
        if (STREQ(g->name, grpname) || STREQ(g->keyword, grpname))
17607 17608 17609 17610 17611 17612
            return g;
    }

    return NULL;
}

E
Eric Blake 已提交
17613
static bool
17614 17615 17616 17617 17618 17619 17620
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 已提交
17621
        return false;
17622 17623 17624 17625 17626 17627 17628 17629 17630 17631
    } 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 已提交
17632
    return true;
17633 17634
}

E
Eric Blake 已提交
17635
static bool
17636
vshCmddefHelp(vshControl *ctl, const char *cmdname)
17637
{
17638
    const vshCmdDef *def = vshCmddefSearch(cmdname);
17639

K
Karel Zak 已提交
17640
    if (!def) {
17641
        vshError(ctl, _("command '%s' doesn't exist"), cmdname);
E
Eric Blake 已提交
17642
        return false;
17643
    } else {
E
Eric Blake 已提交
17644 17645
        /* Don't translate desc if it is "".  */
        const char *desc = vshCmddefGetInfo(def, "desc");
E
Eric Blake 已提交
17646
        const char *help = _(vshCmddefGetInfo(def, "help"));
17647
        char buf[256];
17648 17649
        uint32_t opts_need_arg;
        uint32_t opts_required;
17650
        bool shortopt = false; /* true if 'arg' works instead of '--opt arg' */
17651 17652 17653 17654

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

17658
        fputs(_("  NAME\n"), stdout);
17659 17660
        fprintf(stdout, "    %s - %s\n", def->name, help);

17661 17662 17663 17664 17665
        fputs(_("\n  SYNOPSIS\n"), stdout);
        fprintf(stdout, "    %s", def->name);
        if (def->opts) {
            const vshCmdOptDef *opt;
            for (opt = def->opts; opt->name; opt++) {
17666
                const char *fmt = "%s";
17667 17668
                switch (opt->type) {
                case VSH_OT_BOOL:
17669
                    fmt = "[--%s]";
17670 17671
                    break;
                case VSH_OT_INT:
E
Eric Blake 已提交
17672
                    /* xgettext:c-format */
E
Eric Blake 已提交
17673
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>"
17674
                           : _("[--%s <number>]"));
17675 17676
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
17677 17678
                    break;
                case VSH_OT_STRING:
E
Eric Blake 已提交
17679 17680
                    /* xgettext:c-format */
                    fmt = _("[--%s <string>]");
17681 17682
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
17683 17684
                    break;
                case VSH_OT_DATA:
E
Eric Blake 已提交
17685
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>" : "[<%s>]");
17686 17687
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
17688 17689 17690
                    break;
                case VSH_OT_ARGV:
                    /* xgettext:c-format */
17691 17692 17693 17694 17695 17696 17697 17698
                    if (shortopt) {
                        fmt = (opt->flags & VSH_OFLAG_REQ)
                            ? _("{[--%s] <string>}...")
                            : _("[[--%s] <string>]...");
                    } else {
                        fmt = (opt->flags & VSH_OFLAG_REQ) ? _("<%s>...")
                            : _("[<%s>]...");
                    }
17699
                    break;
E
Eric Blake 已提交
17700 17701 17702
                case VSH_OT_ALIAS:
                    /* aliases are intentionally undocumented */
                    continue;
17703
                default:
17704
                    assert(0);
17705
                }
17706
                fputc(' ', stdout);
E
Eric Blake 已提交
17707
                fprintf(stdout, fmt, opt->name);
17708
            }
K
Karel Zak 已提交
17709
        }
17710 17711 17712
        fputc('\n', stdout);

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

K
Karel Zak 已提交
17718
        if (def->opts) {
17719
            const vshCmdOptDef *opt;
17720
            fputs(_("\n  OPTIONS\n"), stdout);
17721
            for (opt = def->opts; opt->name; opt++) {
17722 17723
                switch (opt->type) {
                case VSH_OT_BOOL:
K
Karel Zak 已提交
17724
                    snprintf(buf, sizeof(buf), "--%s", opt->name);
17725 17726
                    break;
                case VSH_OT_INT:
17727
                    snprintf(buf, sizeof(buf),
E
Eric Blake 已提交
17728
                             (opt->flags & VSH_OFLAG_REQ) ? _("[--%s] <number>")
17729
                             : _("--%s <number>"), opt->name);
17730 17731
                    break;
                case VSH_OT_STRING:
17732
                    /* OT_STRING should never be VSH_OFLAG_REQ */
17733
                    snprintf(buf, sizeof(buf), _("--%s <string>"), opt->name);
17734 17735
                    break;
                case VSH_OT_DATA:
17736 17737
                    snprintf(buf, sizeof(buf), _("[--%s] <string>"),
                             opt->name);
17738 17739
                    break;
                case VSH_OT_ARGV:
17740 17741 17742
                    snprintf(buf, sizeof(buf),
                             shortopt ? _("[--%s] <string>") : _("<%s>"),
                             opt->name);
17743
                    break;
E
Eric Blake 已提交
17744 17745
                case VSH_OT_ALIAS:
                    continue;
17746 17747 17748
                default:
                    assert(0);
                }
17749

E
Eric Blake 已提交
17750
                fprintf(stdout, "    %-15s  %s\n", buf, _(opt->help));
17751
            }
K
Karel Zak 已提交
17752 17753 17754
        }
        fputc('\n', stdout);
    }
E
Eric Blake 已提交
17755
    return true;
K
Karel Zak 已提交
17756 17757 17758 17759 17760 17761
}

/* ---------------
 * Utils for work with runtime commands data
 * ---------------
 */
17762 17763 17764
static void
vshCommandOptFree(vshCmdOpt * arg)
{
K
Karel Zak 已提交
17765 17766
    vshCmdOpt *a = arg;

17767
    while (a) {
K
Karel Zak 已提交
17768
        vshCmdOpt *tmp = a;
17769

K
Karel Zak 已提交
17770 17771
        a = a->next;

17772 17773
        VIR_FREE(tmp->data);
        VIR_FREE(tmp);
K
Karel Zak 已提交
17774 17775 17776 17777
    }
}

static void
17778
vshCommandFree(vshCmd *cmd)
17779
{
K
Karel Zak 已提交
17780 17781
    vshCmd *c = cmd;

17782
    while (c) {
K
Karel Zak 已提交
17783
        vshCmd *tmp = c;
17784

K
Karel Zak 已提交
17785 17786 17787 17788
        c = c->next;

        if (tmp->opts)
            vshCommandOptFree(tmp->opts);
17789
        VIR_FREE(tmp);
K
Karel Zak 已提交
17790 17791 17792
    }
}

E
Eric Blake 已提交
17793 17794 17795 17796 17797 17798 17799 17800 17801 17802 17803
/**
 * 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 已提交
17804
 */
E
Eric Blake 已提交
17805 17806
static int
vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
17807
{
E
Eric Blake 已提交
17808 17809
    vshCmdOpt *candidate = cmd->opts;
    const vshCmdOptDef *valid = cmd->def->opts;
17810

E
Eric Blake 已提交
17811 17812 17813 17814 17815 17816 17817
    /* 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 已提交
17818
    }
E
Eric Blake 已提交
17819 17820 17821 17822 17823 17824 17825

    /* Option not present, see if command requires it.  */
    *opt = NULL;
    while (valid) {
        if (!valid->name)
            break;
        if (STREQ(name, valid->name))
E
Eric Blake 已提交
17826
            return (valid->flags & VSH_OFLAG_REQ) == 0 ? 0 : -1;
E
Eric Blake 已提交
17827 17828 17829 17830
        valid++;
    }
    /* If we got here, the name is unknown.  */
    return -2;
K
Karel Zak 已提交
17831 17832
}

E
Eric Blake 已提交
17833 17834
/**
 * vshCommandOptInt:
17835 17836 17837 17838 17839 17840 17841
 * @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 已提交
17842
 * 0 if option not found and not required (@value untouched)
17843
 * <0 in all other cases (@value untouched)
K
Karel Zak 已提交
17844 17845
 */
static int
17846
vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
17847
{
E
Eric Blake 已提交
17848 17849 17850
    vshCmdOpt *arg;
    int ret;
    int num;
17851
    char *end_p = NULL;
17852

E
Eric Blake 已提交
17853 17854 17855 17856 17857 17858 17859
    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;
17860
    }
E
Eric Blake 已提交
17861 17862 17863 17864 17865 17866 17867

    num = strtol(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
K
Karel Zak 已提交
17868 17869
}

17870

E
Eric Blake 已提交
17871 17872 17873 17874 17875 17876
/**
 * vshCommandOptUInt:
 * @cmd command reference
 * @name option name
 * @value result
 *
17877 17878 17879 17880 17881 17882
 * Convert option to unsigned int
 * See vshCommandOptInt()
 */
static int
vshCommandOptUInt(const vshCmd *cmd, const char *name, unsigned int *value)
{
E
Eric Blake 已提交
17883 17884 17885
    vshCmdOpt *arg;
    int ret;
    unsigned int num;
17886 17887
    char *end_p = NULL;

E
Eric Blake 已提交
17888 17889 17890 17891 17892 17893 17894
    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;
17895
    }
E
Eric Blake 已提交
17896 17897 17898 17899 17900 17901 17902

    num = strtoul(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
17903 17904 17905
}


17906
/*
E
Eric Blake 已提交
17907 17908 17909 17910 17911
 * vshCommandOptUL:
 * @cmd command reference
 * @name option name
 * @value result
 *
17912 17913 17914 17915 17916
 * Convert option to unsigned long
 * See vshCommandOptInt()
 */
static int
vshCommandOptUL(const vshCmd *cmd, const char *name, unsigned long *value)
17917
{
E
Eric Blake 已提交
17918 17919
    vshCmdOpt *arg;
    int ret;
17920
    unsigned long num;
17921 17922
    char *end_p = NULL;

E
Eric Blake 已提交
17923 17924 17925 17926 17927 17928 17929
    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;
17930
    }
E
Eric Blake 已提交
17931 17932 17933 17934 17935 17936 17937

    num = strtoul(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
17938 17939
}

E
Eric Blake 已提交
17940 17941 17942 17943 17944 17945
/**
 * vshCommandOptString:
 * @cmd command reference
 * @name option name
 * @value result
 *
K
Karel Zak 已提交
17946
 * Returns option as STRING
E
Eric Blake 已提交
17947 17948 17949 17950
 * 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 已提交
17951
 */
17952 17953
static int
vshCommandOptString(const vshCmd *cmd, const char *name, const char **value)
17954
{
E
Eric Blake 已提交
17955 17956 17957 17958 17959 17960 17961 17962 17963 17964
    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;
17965
    }
17966

E
Eric Blake 已提交
17967
    if (!*arg->data && !(arg->def->flags & VSH_OFLAG_EMPTY_OK)) {
E
Eric Blake 已提交
17968 17969 17970 17971
        return -1;
    }
    *value = arg->data;
    return 1;
K
Karel Zak 已提交
17972 17973
}

E
Eric Blake 已提交
17974 17975 17976 17977 17978 17979
/**
 * vshCommandOptLongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
17980
 * Returns option as long long
17981
 * See vshCommandOptInt()
17982
 */
17983 17984 17985
static int
vshCommandOptLongLong(const vshCmd *cmd, const char *name,
                      long long *value)
17986
{
E
Eric Blake 已提交
17987 17988
    vshCmdOpt *arg;
    int ret;
17989
    long long num;
17990 17991
    char *end_p = NULL;

E
Eric Blake 已提交
17992 17993 17994 17995 17996 17997 17998
    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;
17999
    }
E
Eric Blake 已提交
18000 18001 18002 18003 18004 18005 18006

    num = strtoll(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
18007 18008
}

E
Eric Blake 已提交
18009 18010 18011 18012 18013 18014 18015 18016 18017
/**
 * vshCommandOptULongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
 * Returns option as long long
 * See vshCommandOptInt()
 */
18018 18019 18020 18021
static int
vshCommandOptULongLong(const vshCmd *cmd, const char *name,
                       unsigned long long *value)
{
E
Eric Blake 已提交
18022 18023
    vshCmdOpt *arg;
    int ret;
18024 18025 18026
    unsigned long long num;
    char *end_p = NULL;

E
Eric Blake 已提交
18027 18028 18029 18030 18031 18032 18033
    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;
18034
    }
E
Eric Blake 已提交
18035 18036 18037 18038 18039 18040 18041

    num = strtoull(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
18042 18043 18044
}


E
Eric Blake 已提交
18045 18046 18047 18048 18049 18050 18051 18052 18053 18054 18055 18056 18057 18058 18059 18060 18061 18062 18063 18064 18065 18066 18067 18068 18069 18070 18071 18072 18073 18074
/**
 * 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 已提交
18075 18076 18077 18078 18079 18080 18081 18082 18083
/**
 * 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 已提交
18084
 */
E
Eric Blake 已提交
18085
static bool
18086
vshCommandOptBool(const vshCmd *cmd, const char *name)
18087
{
E
Eric Blake 已提交
18088 18089 18090
    vshCmdOpt *dummy;

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

E
Eric Blake 已提交
18093 18094 18095 18096 18097
/**
 * vshCommandOptArgv:
 * @cmd command reference
 * @opt starting point for the search
 *
18098 18099
 * Returns the next argv argument after OPT (or the first one if OPT
 * is NULL), or NULL if no more are present.
18100
 *
18101
 * Requires that a VSH_OT_ARGV option be last in the
18102 18103
 * list of supported options in CMD->def->opts.
 */
18104 18105
static const vshCmdOpt *
vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt)
18106
{
18107
    opt = opt ? opt->next : cmd->opts;
18108 18109

    while (opt) {
E
Eric Blake 已提交
18110
        if (opt->def->type == VSH_OT_ARGV) {
18111
            return opt;
18112 18113 18114 18115 18116 18117
        }
        opt = opt->next;
    }
    return NULL;
}

J
Jim Meyering 已提交
18118 18119 18120 18121 18122 18123 18124 18125 18126 18127 18128 18129 18130 18131 18132 18133 18134 18135
/* Determine whether CMD->opts includes an option with name OPTNAME.
   If not, give a diagnostic and return false.
   If so, return true.  */
static bool
cmd_has_option (vshControl *ctl, const vshCmd *cmd, const char *optname)
{
    /* 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) {
        if (STREQ (opt->def->name, optname) && opt->def->type == VSH_OT_DATA) {
            found = true;
            break;
        }
    }

    if (!found)
18136
        vshError(ctl, _("internal error: virsh %s: no %s VSH_OT_DATA option"),
J
Jim Meyering 已提交
18137 18138 18139
                 cmd->def->name, optname);
    return found;
}
18140

K
Karel Zak 已提交
18141
static virDomainPtr
J
Jim Meyering 已提交
18142
vshCommandOptDomainBy(vshControl *ctl, const vshCmd *cmd,
18143
                      const char **name, int flag)
18144
{
K
Karel Zak 已提交
18145
    virDomainPtr dom = NULL;
18146
    const char *n = NULL;
K
Karel Zak 已提交
18147
    int id;
J
Jim Meyering 已提交
18148 18149 18150
    const char *optname = "domain";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;
18151

18152
    if (vshCommandOptString(cmd, optname, &n) <= 0)
18153 18154
        return NULL;

18155
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
18156 18157
             cmd->def->name, optname, n);

K
Karel Zak 已提交
18158 18159
    if (name)
        *name = n;
18160

K
Karel Zak 已提交
18161
    /* try it by ID */
18162
    if (flag & VSH_BYID) {
18163
        if (virStrToLong_i(n, NULL, 10, &id) == 0 && id >= 0) {
18164 18165
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: <%s> seems like domain ID\n",
K
Karel Zak 已提交
18166 18167 18168
                     cmd->def->name, optname);
            dom = virDomainLookupByID(ctl->conn, id);
        }
18169
    }
K
Karel Zak 已提交
18170
    /* try it by UUID */
18171
    if (dom==NULL && (flag & VSH_BYUUID) && strlen(n)==VIR_UUID_STRING_BUFLEN-1) {
18172
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain UUID\n",
18173
                 cmd->def->name, optname);
K
Karel Zak 已提交
18174
        dom = virDomainLookupByUUIDString(ctl->conn, n);
K
Karel Zak 已提交
18175
    }
K
Karel Zak 已提交
18176
    /* try it by NAME */
18177
    if (dom==NULL && (flag & VSH_BYNAME)) {
18178
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain NAME\n",
18179
                 cmd->def->name, optname);
K
Karel Zak 已提交
18180
        dom = virDomainLookupByName(ctl->conn, n);
18181
    }
K
Karel Zak 已提交
18182

18183
    if (!dom)
18184
        vshError(ctl, _("failed to get domain '%s'"), n);
18185

K
Karel Zak 已提交
18186 18187 18188
    return dom;
}

18189
static virNetworkPtr
J
Jim Meyering 已提交
18190
vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
18191
                       const char **name, int flag)
18192 18193
{
    virNetworkPtr network = NULL;
18194
    const char *n = NULL;
J
Jim Meyering 已提交
18195 18196 18197
    const char *optname = "network";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;
18198

18199
    if (vshCommandOptString(cmd, optname, &n) <= 0)
18200 18201
        return NULL;

18202
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
18203 18204 18205 18206 18207 18208
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
18209
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
18210
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
18211
                 cmd->def->name, optname);
18212 18213 18214 18215
        network = virNetworkLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
    if (network==NULL && (flag & VSH_BYNAME)) {
18216
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network NAME\n",
18217 18218 18219 18220 18221
                 cmd->def->name, optname);
        network = virNetworkLookupByName(ctl->conn, n);
    }

    if (!network)
18222
        vshError(ctl, _("failed to get network '%s'"), n);
18223 18224 18225 18226

    return network;
}

18227 18228 18229

static virNWFilterPtr
vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd,
18230
                        const char **name, int flag)
18231 18232
{
    virNWFilterPtr nwfilter = NULL;
18233
    const char *n = NULL;
18234 18235 18236 18237
    const char *optname = "nwfilter";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;

18238
    if (vshCommandOptString(cmd, optname, &n) <= 0)
18239 18240
        return NULL;

18241
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
18242 18243 18244 18245 18246 18247 18248
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
18249
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as nwfilter UUID\n",
18250 18251 18252 18253 18254
                 cmd->def->name, optname);
        nwfilter = virNWFilterLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
    if (nwfilter == NULL && (flag & VSH_BYNAME)) {
18255
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as nwfilter NAME\n",
18256 18257 18258 18259 18260 18261 18262 18263 18264 18265
                 cmd->def->name, optname);
        nwfilter = virNWFilterLookupByName(ctl->conn, n);
    }

    if (!nwfilter)
        vshError(ctl, _("failed to get nwfilter '%s'"), n);

    return nwfilter;
}

18266 18267
static virInterfacePtr
vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd,
18268
                         const char *optname,
18269
                         const char **name, int flag)
18270 18271
{
    virInterfacePtr iface = NULL;
18272
    const char *n = NULL;
18273 18274 18275

    if (!optname)
       optname = "interface";
18276 18277 18278
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;

18279
    if (vshCommandOptString(cmd, optname, &n) <= 0)
18280 18281
        return NULL;

18282
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
18283 18284 18285 18286 18287 18288
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by NAME */
18289
    if ((flag & VSH_BYNAME)) {
18290
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface NAME\n",
18291 18292 18293 18294 18295
                 cmd->def->name, optname);
        iface = virInterfaceLookupByName(ctl->conn, n);
    }
    /* try it by MAC */
    if ((iface == NULL) && (flag & VSH_BYMAC)) {
18296
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface MAC\n",
18297 18298 18299 18300 18301
                 cmd->def->name, optname);
        iface = virInterfaceLookupByMACString(ctl->conn, n);
    }

    if (!iface)
18302
        vshError(ctl, _("failed to get interface '%s'"), n);
18303 18304 18305 18306

    return iface;
}

18307
static virStoragePoolPtr
18308
vshCommandOptPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname,
18309
                    const char **name, int flag)
18310 18311
{
    virStoragePoolPtr pool = NULL;
18312
    const char *n = NULL;
18313

18314
    if (vshCommandOptString(cmd, optname, &n) <= 0)
18315 18316
        return NULL;

18317
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
18318 18319 18320 18321 18322 18323
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
18324
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
18325
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool UUID\n",
18326
                 cmd->def->name, optname);
18327 18328 18329
        pool = virStoragePoolLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
18330
    if (pool == NULL && (flag & VSH_BYNAME)) {
18331
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool NAME\n",
18332 18333 18334 18335 18336
                 cmd->def->name, optname);
        pool = virStoragePoolLookupByName(ctl->conn, n);
    }

    if (!pool)
18337
        vshError(ctl, _("failed to get pool '%s'"), n);
18338 18339 18340 18341 18342

    return pool;
}

static virStorageVolPtr
18343
vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd,
18344 18345
                   const char *optname,
                   const char *pooloptname,
18346
                   const char **name, int flag)
18347 18348 18349
{
    virStorageVolPtr vol = NULL;
    virStoragePoolPtr pool = NULL;
18350
    const char *n = NULL, *p = NULL;
18351

18352
    if (vshCommandOptString(cmd, optname, &n) <= 0)
18353 18354
        return NULL;

18355
    if (pooloptname != NULL && vshCommandOptString(cmd, pooloptname, &p) < 0) {
18356
        vshError(ctl, "%s", _("missing option"));
18357
        return NULL;
18358
    }
18359 18360 18361 18362

    if (p)
        pool = vshCommandOptPoolBy(ctl, cmd, pooloptname, name, flag);

18363
    vshDebug(ctl, VSH_ERR_DEBUG, "%s: found option <%s>: %s\n",
18364 18365 18366 18367 18368
             cmd->def->name, optname, n);

    if (name)
        *name = n;

18369
    /* try it by name */
18370
    if (pool && (flag & VSH_BYNAME)) {
18371
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol name\n",
18372 18373 18374
                 cmd->def->name, optname);
        vol = virStorageVolLookupByName(pool, n);
    }
18375
    /* try it by key */
18376
    if (vol == NULL && (flag & VSH_BYUUID)) {
18377
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol key\n",
18378 18379 18380
                 cmd->def->name, optname);
        vol = virStorageVolLookupByKey(ctl->conn, n);
    }
18381
    /* try it by path */
18382
    if (vol == NULL && (flag & VSH_BYUUID)) {
18383
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol path\n",
18384 18385 18386 18387
                 cmd->def->name, optname);
        vol = virStorageVolLookupByPath(ctl->conn, n);
    }

18388 18389 18390 18391
    if (!vol) {
        if (pool)
            vshError(ctl, _("failed to get vol '%s'"), n);
        else
18392 18393
            vshError(ctl, _("failed to get vol '%s', specifying --%s "
                            "might help"), n, pooloptname);
18394
    }
18395 18396 18397 18398 18399 18400 18401

    if (pool)
        virStoragePoolFree(pool);

    return vol;
}

18402
static virSecretPtr
18403
vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, const char **name)
18404 18405
{
    virSecretPtr secret = NULL;
18406
    const char *n = NULL;
18407 18408 18409 18410 18411
    const char *optname = "secret";

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

18412
    if (vshCommandOptString(cmd, optname, &n) <= 0)
18413 18414
        return NULL;

18415 18416
    vshDebug(ctl, VSH_ERR_DEBUG,
             "%s: found option <%s>: %s\n", cmd->def->name, optname, n);
18417 18418 18419 18420 18421 18422 18423

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

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

    if (secret == NULL)
18424
        vshError(ctl, _("failed to get secret '%s'"), n);
18425 18426 18427 18428

    return secret;
}

K
Karel Zak 已提交
18429 18430 18431
/*
 * Executes command(s) and returns return code from last command
 */
E
Eric Blake 已提交
18432
static bool
18433
vshCommandRun(vshControl *ctl, const vshCmd *cmd)
18434
{
E
Eric Blake 已提交
18435
    bool ret = true;
18436 18437

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

18441 18442
        if ((ctl->conn == NULL || disconnected) &&
            !(cmd->def->flags & VSH_CMD_FLAG_NOCONNECT))
18443 18444
            vshReconnect(ctl);

18445
        if (enable_timing)
K
Karel Zak 已提交
18446
            GETTIMEOFDAY(&before);
18447

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

18450
        if (enable_timing)
K
Karel Zak 已提交
18451
            GETTIMEOFDAY(&after);
18452

18453 18454 18455 18456 18457 18458 18459 18460 18461 18462
        /* 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++;

18463
        if (!ret)
J
John Levon 已提交
18464 18465
            virshReportError(ctl);

18466
        if (!ret && disconnected != 0)
18467 18468
            vshReconnect(ctl);

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

18472
        if (enable_timing)
18473
            vshPrint(ctl, _("\n(Time: %.3f ms)\n\n"),
18474 18475
                     DIFF_MSEC(&after, &before));
        else
K
Karel Zak 已提交
18476
            vshPrintExtra(ctl, "\n");
K
Karel Zak 已提交
18477 18478 18479 18480 18481 18482
        cmd = cmd->next;
    }
    return ret;
}

/* ---------------
18483
 * Command parsing
K
Karel Zak 已提交
18484 18485 18486
 * ---------------
 */

18487 18488 18489 18490 18491 18492 18493 18494 18495 18496
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 {
    vshCommandToken (*getNextArg)(vshControl *, struct __vshCommandParser *,
                                  char **);
L
Lai Jiangshan 已提交
18497
    /* vshCommandStringGetArg() */
18498
    char *pos;
L
Lai Jiangshan 已提交
18499 18500 18501
    /* vshCommandArgvGetArg() */
    char **arg_pos;
    char **arg_end;
18502 18503
} vshCommandParser;

E
Eric Blake 已提交
18504
static bool
18505
vshCommandParse(vshControl *ctl, vshCommandParser *parser)
18506
{
K
Karel Zak 已提交
18507 18508 18509
    char *tkdata = NULL;
    vshCmd *clast = NULL;
    vshCmdOpt *first = NULL;
18510

K
Karel Zak 已提交
18511 18512 18513 18514
    if (ctl->cmd) {
        vshCommandFree(ctl->cmd);
        ctl->cmd = NULL;
    }
18515

18516
    while (1) {
K
Karel Zak 已提交
18517
        vshCmdOpt *last = NULL;
18518
        const vshCmdDef *cmd = NULL;
18519
        vshCommandToken tk;
L
Lai Jiangshan 已提交
18520
        bool data_only = false;
18521 18522 18523
        uint32_t opts_need_arg = 0;
        uint32_t opts_required = 0;
        uint32_t opts_seen = 0;
18524

K
Karel Zak 已提交
18525
        first = NULL;
18526

18527
        while (1) {
18528
            const vshCmdOptDef *opt = NULL;
18529

K
Karel Zak 已提交
18530
            tkdata = NULL;
18531
            tk = parser->getNextArg(ctl, parser, &tkdata);
18532 18533

            if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
18534
                goto syntaxError;
H
Hu Tao 已提交
18535 18536
            if (tk != VSH_TK_ARG) {
                VIR_FREE(tkdata);
18537
                break;
H
Hu Tao 已提交
18538
            }
18539 18540

            if (cmd == NULL) {
K
Karel Zak 已提交
18541 18542
                /* first token must be command name */
                if (!(cmd = vshCmddefSearch(tkdata))) {
18543
                    vshError(ctl, _("unknown command: '%s'"), tkdata);
18544
                    goto syntaxError;   /* ... or ignore this command only? */
K
Karel Zak 已提交
18545
                }
18546 18547 18548 18549 18550 18551 18552
                if (vshCmddefOptParse(cmd, &opts_need_arg,
                                      &opts_required) < 0) {
                    vshError(ctl,
                             _("internal error: bad options in command: '%s'"),
                             tkdata);
                    goto syntaxError;
                }
18553
                VIR_FREE(tkdata);
L
Lai Jiangshan 已提交
18554 18555 18556 18557
            } else if (data_only) {
                goto get_data;
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       c_isalnum(tkdata[2])) {
18558
                char *optstr = strchr(tkdata + 2, '=');
18559 18560
                int opt_index;

18561 18562 18563 18564
                if (optstr) {
                    *optstr = '\0'; /* convert the '=' to '\0' */
                    optstr = vshStrdup(ctl, optstr + 1);
                }
18565
                if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2,
18566
                                               &opts_seen, &opt_index))) {
18567
                    VIR_FREE(optstr);
K
Karel Zak 已提交
18568 18569
                    goto syntaxError;
                }
18570
                VIR_FREE(tkdata);
K
Karel Zak 已提交
18571 18572 18573

                if (opt->type != VSH_OT_BOOL) {
                    /* option data */
18574 18575 18576
                    if (optstr)
                        tkdata = optstr;
                    else
18577
                        tk = parser->getNextArg(ctl, parser, &tkdata);
18578
                    if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
18579
                        goto syntaxError;
18580
                    if (tk != VSH_TK_ARG) {
18581
                        vshError(ctl,
18582
                                 _("expected syntax: --%s <%s>"),
18583 18584
                                 opt->name,
                                 opt->type ==
18585
                                 VSH_OT_INT ? _("number") : _("string"));
K
Karel Zak 已提交
18586 18587
                        goto syntaxError;
                    }
18588 18589
                    if (opt->type != VSH_OT_ARGV)
                        opts_need_arg &= ~(1 << opt_index);
18590 18591 18592 18593 18594 18595 18596 18597
                } else {
                    tkdata = NULL;
                    if (optstr) {
                        vshError(ctl, _("invalid '=' after option --%s"),
                                opt->name);
                        VIR_FREE(optstr);
                        goto syntaxError;
                    }
K
Karel Zak 已提交
18598
                }
L
Lai Jiangshan 已提交
18599 18600 18601 18602
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       tkdata[2] == '\0') {
                data_only = true;
                continue;
18603
            } else {
L
Lai Jiangshan 已提交
18604
get_data:
18605 18606
                if (!(opt = vshCmddefGetData(cmd, &opts_need_arg,
                                             &opts_seen))) {
18607
                    vshError(ctl, _("unexpected data '%s'"), tkdata);
K
Karel Zak 已提交
18608 18609 18610 18611 18612
                    goto syntaxError;
                }
            }
            if (opt) {
                /* save option */
18613
                vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt));
18614

K
Karel Zak 已提交
18615 18616 18617 18618
                arg->def = opt;
                arg->data = tkdata;
                arg->next = NULL;
                tkdata = NULL;
18619

K
Karel Zak 已提交
18620 18621 18622 18623 18624
                if (!first)
                    first = arg;
                if (last)
                    last->next = arg;
                last = arg;
18625

18626
                vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n",
18627 18628
                         cmd->name,
                         opt->name,
18629 18630
                         opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"),
                         opt->type != VSH_OT_BOOL ? arg->data : _("(none)"));
K
Karel Zak 已提交
18631 18632
            }
        }
18633

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

K
Karel Zak 已提交
18638 18639 18640 18641
            c->opts = first;
            c->def = cmd;
            c->next = NULL;

18642
            if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) {
18643
                VIR_FREE(c);
18644
                goto syntaxError;
18645
            }
18646

K
Karel Zak 已提交
18647 18648 18649 18650 18651 18652
            if (!ctl->cmd)
                ctl->cmd = c;
            if (clast)
                clast->next = c;
            clast = c;
        }
18653 18654 18655

        if (tk == VSH_TK_END)
            break;
K
Karel Zak 已提交
18656
    }
18657

E
Eric Blake 已提交
18658
    return true;
K
Karel Zak 已提交
18659

18660
 syntaxError:
18661
    if (ctl->cmd) {
K
Karel Zak 已提交
18662
        vshCommandFree(ctl->cmd);
18663 18664
        ctl->cmd = NULL;
    }
K
Karel Zak 已提交
18665 18666
    if (first)
        vshCommandOptFree(first);
18667
    VIR_FREE(tkdata);
E
Eric Blake 已提交
18668
    return false;
K
Karel Zak 已提交
18669 18670
}

18671 18672 18673 18674 18675 18676 18677 18678 18679 18680 18681 18682 18683 18684 18685 18686 18687 18688
/* --------------------
 * 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 已提交
18689 18690
static bool
vshCommandArgvParse(vshControl *ctl, int nargs, char **argv)
18691 18692 18693 18694
{
    vshCommandParser parser;

    if (nargs <= 0)
E
Eric Blake 已提交
18695
        return false;
18696 18697 18698 18699 18700 18701 18702 18703 18704 18705 18706 18707 18708 18709 18710 18711 18712 18713 18714 18715 18716 18717 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 18734 18735 18736 18737 18738 18739 18740 18741 18742 18743 18744 18745 18746 18747 18748 18749 18750 18751 18752 18753 18754 18755 18756 18757 18758 18759 18760 18761 18762 18763 18764 18765 18766 18767

    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 已提交
18768 18769
static bool
vshCommandStringParse(vshControl *ctl, char *cmdstr)
18770 18771 18772 18773
{
    vshCommandParser parser;

    if (cmdstr == NULL || *cmdstr == '\0')
E
Eric Blake 已提交
18774
        return false;
18775 18776 18777 18778 18779 18780

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

K
Karel Zak 已提交
18781
/* ---------------
18782
 * Misc utils
K
Karel Zak 已提交
18783 18784
 * ---------------
 */
18785 18786 18787 18788 18789 18790 18791 18792 18793 18794 18795 18796 18797 18798 18799 18800 18801 18802 18803 18804 18805 18806 18807 18808 18809 18810 18811 18812
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;
}

K
Karel Zak 已提交
18813
static const char *
18814 18815
vshDomainStateToString(int state)
{
18816 18817
    /* Can't use virDomainStateTypeToString, because we want to mark
     * strings for translation.  */
18818
    switch ((virDomainState) state) {
18819
    case VIR_DOMAIN_RUNNING:
18820
        return N_("running");
18821
    case VIR_DOMAIN_BLOCKED:
18822
        return N_("idle");
18823
    case VIR_DOMAIN_PAUSED:
18824
        return N_("paused");
18825
    case VIR_DOMAIN_SHUTDOWN:
18826
        return N_("in shutdown");
18827
    case VIR_DOMAIN_SHUTOFF:
18828
        return N_("shut off");
18829
    case VIR_DOMAIN_CRASHED:
18830
        return N_("crashed");
O
Osier Yang 已提交
18831 18832
    case VIR_DOMAIN_PMSUSPENDED:
        return N_("pmsuspended");
18833
    case VIR_DOMAIN_NOSTATE:
18834
    default:
18835
        ;/*FALLTHROUGH*/
K
Karel Zak 已提交
18836
    }
18837
    return N_("no state");  /* = dom0 state */
K
Karel Zak 已提交
18838 18839
}

18840 18841 18842 18843 18844 18845 18846
static const char *
vshDomainStateReasonToString(int state, int reason)
{
    switch ((virDomainState) state) {
    case VIR_DOMAIN_NOSTATE:
        switch ((virDomainNostateReason) reason) {
        case VIR_DOMAIN_NOSTATE_UNKNOWN:
18847
        case VIR_DOMAIN_NOSTATE_LAST:
18848 18849 18850 18851 18852 18853 18854 18855 18856 18857 18858 18859 18860 18861 18862 18863 18864 18865 18866 18867
            ;
        }
        break;

    case VIR_DOMAIN_RUNNING:
        switch ((virDomainRunningReason) reason) {
        case VIR_DOMAIN_RUNNING_BOOTED:
            return N_("booted");
        case VIR_DOMAIN_RUNNING_MIGRATED:
            return N_("migrated");
        case VIR_DOMAIN_RUNNING_RESTORED:
            return N_("restored");
        case VIR_DOMAIN_RUNNING_FROM_SNAPSHOT:
            return N_("from snapshot");
        case VIR_DOMAIN_RUNNING_UNPAUSED:
            return N_("unpaused");
        case VIR_DOMAIN_RUNNING_MIGRATION_CANCELED:
            return N_("migration canceled");
        case VIR_DOMAIN_RUNNING_SAVE_CANCELED:
            return N_("save canceled");
18868 18869
        case VIR_DOMAIN_RUNNING_WAKEUP:
            return N_("event wakeup");
18870
        case VIR_DOMAIN_RUNNING_UNKNOWN:
18871
        case VIR_DOMAIN_RUNNING_LAST:
18872 18873 18874 18875 18876 18877 18878
            ;
        }
        break;

    case VIR_DOMAIN_BLOCKED:
        switch ((virDomainBlockedReason) reason) {
        case VIR_DOMAIN_BLOCKED_UNKNOWN:
18879
        case VIR_DOMAIN_BLOCKED_LAST:
18880 18881 18882 18883 18884 18885 18886 18887 18888 18889 18890 18891 18892 18893 18894 18895 18896 18897 18898 18899
            ;
        }
        break;

    case VIR_DOMAIN_PAUSED:
        switch ((virDomainPausedReason) reason) {
        case VIR_DOMAIN_PAUSED_USER:
            return N_("user");
        case VIR_DOMAIN_PAUSED_MIGRATION:
            return N_("migrating");
        case VIR_DOMAIN_PAUSED_SAVE:
            return N_("saving");
        case VIR_DOMAIN_PAUSED_DUMP:
            return N_("dumping");
        case VIR_DOMAIN_PAUSED_IOERROR:
            return N_("I/O error");
        case VIR_DOMAIN_PAUSED_WATCHDOG:
            return N_("watchdog");
        case VIR_DOMAIN_PAUSED_FROM_SNAPSHOT:
            return N_("from snapshot");
18900 18901
        case VIR_DOMAIN_PAUSED_SHUTTING_DOWN:
            return N_("shutting down");
18902
        case VIR_DOMAIN_PAUSED_UNKNOWN:
18903
        case VIR_DOMAIN_PAUSED_LAST:
18904 18905 18906 18907 18908 18909 18910 18911 18912
            ;
        }
        break;

    case VIR_DOMAIN_SHUTDOWN:
        switch ((virDomainShutdownReason) reason) {
        case VIR_DOMAIN_SHUTDOWN_USER:
            return N_("user");
        case VIR_DOMAIN_SHUTDOWN_UNKNOWN:
18913
        case VIR_DOMAIN_SHUTDOWN_LAST:
18914 18915 18916 18917 18918 18919 18920 18921 18922 18923 18924 18925 18926 18927 18928 18929 18930 18931 18932 18933 18934
            ;
        }
        break;

    case VIR_DOMAIN_SHUTOFF:
        switch ((virDomainShutoffReason) reason) {
        case VIR_DOMAIN_SHUTOFF_SHUTDOWN:
            return N_("shutdown");
        case VIR_DOMAIN_SHUTOFF_DESTROYED:
            return N_("destroyed");
        case VIR_DOMAIN_SHUTOFF_CRASHED:
            return N_("crashed");
        case VIR_DOMAIN_SHUTOFF_MIGRATED:
            return N_("migrated");
        case VIR_DOMAIN_SHUTOFF_SAVED:
            return N_("saved");
        case VIR_DOMAIN_SHUTOFF_FAILED:
            return N_("failed");
        case VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT:
            return N_("from snapshot");
        case VIR_DOMAIN_SHUTOFF_UNKNOWN:
18935
        case VIR_DOMAIN_SHUTOFF_LAST:
18936 18937 18938 18939 18940 18941 18942
            ;
        }
        break;

    case VIR_DOMAIN_CRASHED:
        switch ((virDomainCrashedReason) reason) {
        case VIR_DOMAIN_CRASHED_UNKNOWN:
18943
        case VIR_DOMAIN_CRASHED_LAST:
18944 18945 18946
            ;
        }
        break;
18947

O
Osier Yang 已提交
18948 18949 18950 18951 18952 18953 18954 18955
    case VIR_DOMAIN_PMSUSPENDED:
        switch ((virDomainPMSuspendedReason) reason) {
        case VIR_DOMAIN_PMSUSPENDED_UNKNOWN:
        case VIR_DOMAIN_PMSUSPENDED_LAST:
            ;
        }
        break;

18956
    case VIR_DOMAIN_LAST:
18957
        ;
18958 18959 18960 18961 18962
    }

    return N_("unknown");
}

18963 18964 18965 18966 18967 18968 18969 18970 18971 18972 18973 18974 18975 18976 18977 18978 18979 18980 18981 18982 18983 18984 18985 18986 18987 18988 18989 18990 18991 18992 18993 18994 18995 18996 18997 18998 18999 19000 19001 19002 19003 19004 19005 19006 19007 19008 19009 19010 19011 19012 19013 19014 19015 19016 19017 19018 19019 19020 19021
/* extract description or title from domain xml */
static char *
vshGetDomainDescription(vshControl *ctl, virDomainPtr dom, bool title,
                        unsigned int flags)
{
    char *desc = NULL;
    char *domxml = NULL;
    virErrorPtr err = NULL;
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    int type;

    if (title)
        type = VIR_DOMAIN_METADATA_TITLE;
    else
        type = VIR_DOMAIN_METADATA_DESCRIPTION;

    if ((desc = virDomainGetMetadata(dom, type, NULL, flags))) {
        return desc;
    } else {
        err = virGetLastError();

        if (err && err->code == VIR_ERR_NO_DOMAIN_METADATA) {
            desc = vshStrdup(ctl, "");
            virResetLastError();
            return desc;
        }

        if (err && err->code != VIR_ERR_NO_SUPPORT)
            return desc;
    }

    /* fall back to xml */
    /* get domain's xml description and extract the title/description */
    if (!(domxml = virDomainGetXMLDesc(dom, flags))) {
        vshError(ctl, "%s", _("Failed to retrieve domain XML"));
        goto cleanup;
    }
    doc = virXMLParseStringCtxt(domxml, _("(domain_definition)"), &ctxt);
    if (!doc) {
        vshError(ctl, "%s", _("Couldn't parse domain XML"));
        goto cleanup;
    }
    if (title)
        desc = virXPathString("string(./title[1])", ctxt);
    else
        desc = virXPathString("string(./description[1])", ctxt);

    if (!desc)
        desc = vshStrdup(ctl, "");

cleanup:
    VIR_FREE(domxml);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(doc);

    return desc;
}

19022 19023
/* Return a non-NULL string representation of a typed parameter; exit
 * if we are out of memory.  */
19024 19025 19026 19027 19028 19029 19030 19031 19032 19033 19034 19035 19036 19037 19038 19039 19040 19041 19042 19043 19044 19045 19046 19047 19048 19049 19050 19051 19052 19053 19054
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;

19055 19056 19057 19058
    case VIR_TYPED_PARAM_STRING:
        str = vshStrdup(ctl, item->value.s);
        break;

19059
    default:
19060
        vshError(ctl, _("unimplemented parameter type %d"), item->type);
19061 19062
    }

19063
    if (ret < 0) {
19064
        vshError(ctl, "%s", _("Out of memory"));
19065 19066
        exit(EXIT_FAILURE);
    }
19067 19068 19069 19070 19071 19072 19073 19074 19075 19076 19077 19078 19079 19080 19081 19082 19083 19084 19085 19086 19087 19088 19089
    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;
}

19090 19091 19092 19093 19094 19095 19096 19097 19098 19099 19100 19101
static const char *
vshDomainControlStateToString(int state)
{
    switch ((virDomainControlState) state) {
    case VIR_DOMAIN_CONTROL_OK:
        return N_("ok");
    case VIR_DOMAIN_CONTROL_JOB:
        return N_("background job");
    case VIR_DOMAIN_CONTROL_OCCUPIED:
        return N_("occupied");
    case VIR_DOMAIN_CONTROL_ERROR:
        return N_("error");
19102 19103
    default:
        ;
19104 19105 19106 19107 19108
    }

    return N_("unknown");
}

19109 19110 19111 19112
static const char *
vshDomainVcpuStateToString(int state)
{
    switch (state) {
19113
    case VIR_VCPU_OFFLINE:
19114
        return N_("offline");
19115
    case VIR_VCPU_BLOCKED:
19116
        return N_("idle");
19117
    case VIR_VCPU_RUNNING:
19118
        return N_("running");
19119
    default:
19120
        ;/*FALLTHROUGH*/
19121
    }
19122
    return N_("no state");
19123 19124
}

E
Eric Blake 已提交
19125
static bool
19126
vshConnectionUsability(vshControl *ctl, virConnectPtr conn)
19127
{
19128 19129
    /* TODO: use something like virConnectionState() to
     *       check usability of the connection
K
Karel Zak 已提交
19130 19131
     */
    if (!conn) {
19132
        vshError(ctl, "%s", _("no valid connection"));
E
Eric Blake 已提交
19133
        return false;
K
Karel Zak 已提交
19134
    }
E
Eric Blake 已提交
19135
    return true;
K
Karel Zak 已提交
19136 19137
}

K
Karel Zak 已提交
19138
static void
19139
vshDebug(vshControl *ctl, int level, const char *format, ...)
19140
{
K
Karel Zak 已提交
19141
    va_list ap;
19142
    char *str;
K
Karel Zak 已提交
19143

19144 19145 19146 19147 19148 19149 19150
    /* Aligning log levels to that of libvirt.
     * Traces with levels >=  user-specified-level
     * gets logged into file
     */
    if (level < ctl->debug)
        return;

19151
    va_start(ap, format);
19152
    vshOutputLogFile(ctl, level, format, ap);
19153 19154
    va_end(ap);

K
Karel Zak 已提交
19155
    va_start(ap, format);
19156 19157 19158 19159 19160
    if (virVasprintf(&str, format, ap) < 0) {
        /* Skip debug messages on low memory */
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
19161
    va_end(ap);
19162 19163
    fputs(str, stdout);
    VIR_FREE(str);
K
Karel Zak 已提交
19164 19165 19166
}

static void
19167
vshPrintExtra(vshControl *ctl, const char *format, ...)
19168
{
K
Karel Zak 已提交
19169
    va_list ap;
19170
    char *str;
19171

19172
    if (ctl && ctl->quiet)
K
Karel Zak 已提交
19173
        return;
19174

K
Karel Zak 已提交
19175
    va_start(ap, format);
19176 19177 19178 19179 19180
    if (virVasprintf(&str, format, ap) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
19181
    va_end(ap);
19182
    fputs(str, stdout);
19183
    VIR_FREE(str);
K
Karel Zak 已提交
19184 19185
}

K
Karel Zak 已提交
19186

K
Karel Zak 已提交
19187
static void
19188
vshError(vshControl *ctl, const char *format, ...)
19189
{
K
Karel Zak 已提交
19190
    va_list ap;
19191
    char *str;
19192

19193 19194 19195 19196 19197
    if (ctl != NULL) {
        va_start(ap, format);
        vshOutputLogFile(ctl, VSH_ERR_ERROR, format, ap);
        va_end(ap);
    }
19198

19199 19200 19201 19202
    /* 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);
19203
    fputs(_("error: "), stderr);
19204

K
Karel Zak 已提交
19205
    va_start(ap, format);
19206 19207 19208
    /* We can't recursively call vshError on an OOM situation, so ignore
       failure here. */
    ignore_value(virVasprintf(&str, format, ap));
K
Karel Zak 已提交
19209 19210
    va_end(ap);

19211
    fprintf(stderr, "%s\n", NULLSTR(str));
19212
    fflush(stderr);
19213
    VIR_FREE(str);
K
Karel Zak 已提交
19214 19215
}

19216

J
Jiri Denemark 已提交
19217 19218 19219 19220 19221
static void
vshEventLoop(void *opaque)
{
    vshControl *ctl = opaque;

19222 19223 19224 19225 19226 19227 19228 19229 19230 19231
    while (1) {
        bool quit;
        virMutexLock(&ctl->lock);
        quit = ctl->quit;
        virMutexUnlock(&ctl->lock);

        if (quit)
            break;

        if (virEventRunDefaultImpl() < 0)
J
Jiri Denemark 已提交
19232 19233 19234 19235 19236
            virshReportError(ctl);
    }
}


K
Karel Zak 已提交
19237
/*
19238
 * Initialize connection.
K
Karel Zak 已提交
19239
 */
E
Eric Blake 已提交
19240
static bool
19241
vshInit(vshControl *ctl)
19242
{
19243 19244
    char *debugEnv;

K
Karel Zak 已提交
19245
    if (ctl->conn)
E
Eric Blake 已提交
19246
        return false;
K
Karel Zak 已提交
19247

J
Jiri Denemark 已提交
19248
    if (ctl->debug == VSH_DEBUG_DEFAULT) {
19249 19250 19251
        /* log level not set from commandline, check env variable */
        debugEnv = getenv("VIRSH_DEBUG");
        if (debugEnv) {
J
Jiri Denemark 已提交
19252 19253 19254
            int debug;
            if (virStrToLong_i(debugEnv, NULL, 10, &debug) < 0 ||
                debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) {
19255 19256
                vshError(ctl, "%s",
                         _("VIRSH_DEBUG not set with a valid numeric value"));
J
Jiri Denemark 已提交
19257 19258
            } else {
                ctl->debug = debug;
19259 19260 19261 19262 19263 19264 19265 19266 19267 19268 19269 19270
            }
        }
    }

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

19271 19272
    vshOpenLogFile(ctl);

19273 19274
    /* set up the library error handler */
    virSetErrorFunc(NULL, virshErrorHandler);
19275

19276 19277 19278
    /* set up the signals handlers to catch disconnections */
    vshSetupSignals();

19279
    if (virEventRegisterDefaultImpl() < 0)
E
Eric Blake 已提交
19280
        return false;
19281

J
Jiri Denemark 已提交
19282 19283 19284 19285
    if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0)
        return false;
    ctl->eventLoopStarted = true;

19286 19287 19288 19289
    if (ctl->name) {
        ctl->conn = virConnectOpenAuth(ctl->name,
                                       virConnectAuthPtrDefault,
                                       ctl->readonly ? VIR_CONNECT_RO : 0);
19290

19291 19292 19293 19294 19295 19296 19297 19298 19299 19300 19301
        /* 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;
        }
19302
    }
K
Karel Zak 已提交
19303

E
Eric Blake 已提交
19304
    return true;
K
Karel Zak 已提交
19305 19306
}

19307 19308
#define LOGFILE_FLAGS (O_WRONLY | O_APPEND | O_CREAT | O_SYNC)

19309 19310 19311 19312 19313 19314 19315 19316 19317 19318 19319 19320 19321 19322 19323 19324 19325 19326 19327
/**
 * 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:
19328
                vshError(ctl, "%s",
J
Jim Meyering 已提交
19329
                         _("failed to get the log file information"));
19330
                exit(EXIT_FAILURE);
19331 19332 19333
        }
    } else {
        if (!S_ISREG(st.st_mode)) {
19334 19335
            vshError(ctl, "%s", _("the log path is not a file"));
            exit(EXIT_FAILURE);
19336 19337 19338 19339
        }
    }

    /* log file open */
19340
    if ((ctl->log_fd = open(ctl->logfile, LOGFILE_FLAGS, FILE_MODE)) < 0) {
19341
        vshError(ctl, "%s",
J
Jim Meyering 已提交
19342
                 _("failed to open the log file. check the log file path"));
19343
        exit(EXIT_FAILURE);
19344 19345 19346 19347 19348 19349 19350 19351 19352
    }
}

/**
 * vshOutputLogFile:
 *
 * Outputting an error to log file.
 */
static void
19353 19354
vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format,
                 va_list ap)
19355
{
19356 19357 19358
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *str;
    size_t len;
19359 19360 19361 19362 19363 19364 19365 19366 19367 19368 19369 19370 19371 19372
    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);
19373
    virBufferAsprintf(&buf, "[%d.%02d.%02d %02d:%02d:%02d %s %d] ",
19374 19375 19376 19377 19378 19379
                      (1900 + stTm->tm_year),
                      (1 + stTm->tm_mon),
                      stTm->tm_mday,
                      stTm->tm_hour,
                      stTm->tm_min,
                      stTm->tm_sec,
19380 19381
                      SIGN_NAME,
                      (int) getpid());
19382 19383 19384 19385 19386 19387 19388 19389 19390 19391 19392 19393 19394 19395 19396 19397 19398 19399 19400 19401
    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;
    }
19402 19403 19404
    virBufferAsprintf(&buf, "%s ", lvl);
    virBufferVasprintf(&buf, msg_format, ap);
    virBufferAddChar(&buf, '\n');
19405

19406 19407
    if (virBufferError(&buf))
        goto error;
19408

19409 19410 19411 19412 19413
    str = virBufferContentAndReset(&buf);
    len = strlen(str);
    if (len > 1 && str[len - 2] == '\n') {
        str[len - 1] = '\0';
        len--;
19414
    }
19415

19416 19417 19418 19419 19420 19421 19422 19423 19424 19425 19426
    /* 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);
19427 19428 19429 19430 19431 19432 19433 19434 19435 19436 19437
}

/**
 * vshCloseLogFile:
 *
 * Close log file.
 */
static void
vshCloseLogFile(vshControl *ctl)
{
    /* log file close */
19438 19439 19440
    if (VIR_CLOSE(ctl->log_fd) < 0) {
        vshError(ctl, _("%s: failed to write log file: %s"),
                 ctl->logfile ? ctl->logfile : "?", strerror (errno));
19441 19442 19443
    }

    if (ctl->logfile) {
19444
        VIR_FREE(ctl->logfile);
19445 19446 19447 19448
        ctl->logfile = NULL;
    }
}

19449
#ifdef USE_READLINE
19450

K
Karel Zak 已提交
19451 19452 19453 19454 19455
/* -----------------
 * Readline stuff
 * -----------------
 */

19456
/*
K
Karel Zak 已提交
19457 19458
 * Generator function for command completion.  STATE lets us
 * know whether to start from scratch; without any state
19459
 * (i.e. STATE == 0), then we start at the top of the list.
K
Karel Zak 已提交
19460 19461
 */
static char *
19462 19463
vshReadlineCommandGenerator(const char *text, int state)
{
19464
    static int grp_list_index, cmd_list_index, len;
K
Karel Zak 已提交
19465
    const char *name;
19466 19467
    const vshCmdGrp *grp;
    const vshCmdDef *cmds;
K
Karel Zak 已提交
19468 19469

    if (!state) {
19470 19471
        grp_list_index = 0;
        cmd_list_index = 0;
19472
        len = strlen(text);
K
Karel Zak 已提交
19473 19474
    }

19475 19476
    grp = cmdGroups;

K
Karel Zak 已提交
19477
    /* Return the next name which partially matches from the
19478
     * command list.
K
Karel Zak 已提交
19479
     */
19480 19481 19482 19483 19484 19485 19486 19487 19488 19489 19490 19491 19492 19493
    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 已提交
19494 19495 19496 19497 19498 19499 19500
    }

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

static char *
19501 19502
vshReadlineOptionsGenerator(const char *text, int state)
{
K
Karel Zak 已提交
19503
    static int list_index, len;
19504
    static const vshCmdDef *cmd = NULL;
K
Karel Zak 已提交
19505
    const char *name;
K
Karel Zak 已提交
19506 19507 19508 19509 19510 19511 19512 19513 19514

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

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

19515
        cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1);
19516
        memcpy(cmdname, rl_line_buffer, p - rl_line_buffer);
K
Karel Zak 已提交
19517 19518 19519

        cmd = vshCmddefSearch(cmdname);
        list_index = 0;
19520
        len = strlen(text);
19521
        VIR_FREE(cmdname);
K
Karel Zak 已提交
19522 19523 19524 19525
    }

    if (!cmd)
        return NULL;
19526

19527 19528 19529
    if (!cmd->opts)
        return NULL;

K
Karel Zak 已提交
19530
    while ((name = cmd->opts[list_index].name)) {
19531
        const vshCmdOptDef *opt = &cmd->opts[list_index];
K
Karel Zak 已提交
19532
        char *res;
19533

K
Karel Zak 已提交
19534
        list_index++;
19535

19536
        if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV)
K
Karel Zak 已提交
19537 19538
            /* ignore non --option */
            continue;
19539

K
Karel Zak 已提交
19540
        if (len > 2) {
19541
            if (STRNEQLEN(name, text + 2, len - 2))
K
Karel Zak 已提交
19542 19543
                continue;
        }
19544
        res = vshMalloc(NULL, strlen(name) + 3);
19545
        snprintf(res, strlen(name) + 3,  "--%s", name);
K
Karel Zak 已提交
19546 19547 19548 19549 19550 19551 19552 19553
        return res;
    }

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

static char **
19554 19555 19556
vshReadlineCompletion(const char *text, int start,
                      int end ATTRIBUTE_UNUSED)
{
K
Karel Zak 已提交
19557 19558
    char **matches = (char **) NULL;

19559
    if (start == 0)
K
Karel Zak 已提交
19560
        /* command name generator */
19561
        matches = rl_completion_matches(text, vshReadlineCommandGenerator);
K
Karel Zak 已提交
19562 19563
    else
        /* commands options */
19564
        matches = rl_completion_matches(text, vshReadlineOptionsGenerator);
K
Karel Zak 已提交
19565 19566 19567 19568
    return matches;
}


19569 19570
static int
vshReadlineInit(vshControl *ctl)
19571
{
19572 19573
    char *userdir = NULL;

K
Karel Zak 已提交
19574 19575 19576 19577 19578
    /* 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;
19579 19580 19581

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

    /* Prepare to read/write history from/to the ~/.virsh/history file */
19584
    userdir = virGetUserDirectory(getuid());
19585

19586 19587
    if (userdir == NULL) {
        vshError(ctl, "%s", _("Could not determine home directory"));
19588
        return -1;
19589
    }
19590 19591 19592

    if (virAsprintf(&ctl->historydir, "%s/.virsh", userdir) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
19593
        VIR_FREE(userdir);
19594 19595 19596 19597 19598
        return -1;
    }

    if (virAsprintf(&ctl->historyfile, "%s/history", ctl->historydir) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
19599
        VIR_FREE(userdir);
19600 19601 19602
        return -1;
    }

19603
    VIR_FREE(userdir);
19604 19605 19606 19607 19608 19609 19610 19611 19612 19613 19614 19615 19616 19617 19618 19619 19620 19621

    read_history(ctl->historyfile);

    return 0;
}

static void
vshReadlineDeinit (vshControl *ctl)
{
    if (ctl->historyfile != NULL) {
        if (mkdir(ctl->historydir, 0755) < 0 && errno != EEXIST) {
            char ebuf[1024];
            vshError(ctl, _("Failed to create '%s': %s"),
                     ctl->historydir, virStrerror(errno, ebuf, sizeof ebuf));
        } else
            write_history(ctl->historyfile);
    }

19622 19623
    VIR_FREE(ctl->historydir);
    VIR_FREE(ctl->historyfile);
K
Karel Zak 已提交
19624 19625
}

19626 19627 19628 19629 19630 19631
static char *
vshReadline (vshControl *ctl ATTRIBUTE_UNUSED, const char *prompt)
{
    return readline (prompt);
}

19632
#else /* !USE_READLINE */
19633

19634 19635 19636 19637 19638 19639 19640
static int
vshReadlineInit (vshControl *ctl ATTRIBUTE_UNUSED)
{
    /* empty */
    return 0;
}

19641
static void
19642
vshReadlineDeinit (vshControl *ctl ATTRIBUTE_UNUSED)
19643 19644 19645 19646 19647 19648 19649 19650 19651 19652 19653 19654 19655 19656 19657 19658 19659 19660 19661 19662 19663 19664 19665
{
    /* empty */
}

static char *
vshReadline (vshControl *ctl, const char *prompt)
{
    char line[1024];
    char *r;
    int len;

    fputs (prompt, stdout);
    r = fgets (line, sizeof line, stdin);
    if (r == NULL) return NULL; /* EOF */

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

    return vshStrdup (ctl, r);
}

19666
#endif /* !USE_READLINE */
19667

19668 19669 19670 19671 19672 19673
static void
vshDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED)
{
    /* nothing to be done here */
}

K
Karel Zak 已提交
19674
/*
J
Jim Meyering 已提交
19675
 * Deinitialize virsh
K
Karel Zak 已提交
19676
 */
E
Eric Blake 已提交
19677
static bool
19678
vshDeinit(vshControl *ctl)
19679
{
19680
    vshReadlineDeinit(ctl);
19681
    vshCloseLogFile(ctl);
19682
    VIR_FREE(ctl->name);
K
Karel Zak 已提交
19683
    if (ctl->conn) {
19684 19685 19686
        int ret;
        if ((ret = virConnectClose(ctl->conn)) != 0) {
            vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
K
Karel Zak 已提交
19687 19688
        }
    }
D
Daniel P. Berrange 已提交
19689 19690
    virResetLastError();

J
Jiri Denemark 已提交
19691
    if (ctl->eventLoopStarted) {
19692 19693 19694 19695
        int timer;

        virMutexLock(&ctl->lock);
        ctl->quit = true;
J
Jiri Denemark 已提交
19696
        /* HACK: Add a dummy timeout to break event loop */
19697 19698 19699 19700 19701
        timer = virEventAddTimeout(0, vshDeinitTimer, NULL, NULL);
        virMutexUnlock(&ctl->lock);

        virThreadJoin(&ctl->eventLoop);

J
Jiri Denemark 已提交
19702 19703 19704 19705 19706 19707
        if (timer != -1)
            virEventRemoveTimeout(timer);

        ctl->eventLoopStarted = false;
    }

19708 19709
    virMutexDestroy(&ctl->lock);

E
Eric Blake 已提交
19710
    return true;
K
Karel Zak 已提交
19711
}
19712

K
Karel Zak 已提交
19713 19714 19715 19716
/*
 * Print usage
 */
static void
19717
vshUsage(void)
19718
{
19719
    const vshCmdGrp *grp;
19720
    const vshCmdDef *cmd;
19721

L
Lai Jiangshan 已提交
19722 19723
    fprintf(stdout, _("\n%s [options]... [<command_string>]"
                      "\n%s [options]... <command> [args...]\n\n"
19724
                      "  options:\n"
19725
                      "    -c | --connect=URI      hypervisor connection URI\n"
19726
                      "    -r | --readonly         connect readonly\n"
19727
                      "    -d | --debug=NUM        debug level [0-4]\n"
19728 19729 19730
                      "    -h | --help             this help\n"
                      "    -q | --quiet            quiet mode\n"
                      "    -t | --timing           print timing information\n"
19731 19732 19733 19734 19735
                      "    -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"
19736
                      "  commands (non interactive mode):\n\n"), progname, progname);
19737

19738
    for (grp = cmdGroups; grp->name; grp++) {
E
Eric Blake 已提交
19739 19740 19741 19742 19743
        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;
19744
            fprintf(stdout,
E
Eric Blake 已提交
19745 19746 19747
                    "    %-30s %s\n", cmd->name,
                    _(vshCmddefGetInfo(cmd, "help")));
        }
19748 19749 19750 19751 19752
        fprintf(stdout, "\n");
    }

    fprintf(stdout, "%s",
            _("\n  (specify help <group> for details about the commands in the group)\n"));
19753 19754 19755
    fprintf(stdout, "%s",
            _("\n  (specify help <command> for details about the command)\n\n"));
    return;
K
Karel Zak 已提交
19756 19757
}

19758 19759 19760 19761 19762 19763 19764 19765 19766 19767
/*
 * 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 已提交
19768 19769
    vshPrint(ctl, "%s", _("Compiled with support for:\n"));
    vshPrint(ctl, "%s", _(" Hypervisors:"));
19770 19771 19772 19773 19774 19775 19776 19777 19778 19779 19780 19781 19782 19783 19784 19785 19786 19787 19788 19789 19790 19791 19792 19793 19794 19795 19796 19797 19798 19799 19800 19801 19802 19803 19804
#ifdef WITH_XEN
    vshPrint(ctl, " Xen");
#endif
#ifdef WITH_QEMU
    vshPrint(ctl, " QEmu/KVM");
#endif
#ifdef WITH_UML
    vshPrint(ctl, " UML");
#endif
#ifdef WITH_OPENVZ
    vshPrint(ctl, " OpenVZ");
#endif
#ifdef WITH_VBOX
    vshPrint(ctl, " VirtualBox");
#endif
#ifdef WITH_XENAPI
    vshPrint(ctl, " XenAPI");
#endif
#ifdef WITH_LXC
    vshPrint(ctl, " LXC");
#endif
#ifdef WITH_ESX
    vshPrint(ctl, " ESX");
#endif
#ifdef WITH_PHYP
    vshPrint(ctl, " PHYP");
#endif
#ifdef WITH_ONE
    vshPrint(ctl, " ONE");
#endif
#ifdef WITH_TEST
    vshPrint(ctl, " Test");
#endif
    vshPrint(ctl, "\n");

L
Laine Stump 已提交
19805
    vshPrint(ctl, "%s", _(" Networking:"));
19806 19807 19808 19809 19810 19811 19812 19813 19814 19815 19816 19817 19818 19819 19820 19821 19822 19823 19824 19825 19826 19827 19828 19829 19830 19831
#ifdef WITH_REMOTE
    vshPrint(ctl, " Remote");
#endif
#ifdef WITH_PROXY
    vshPrint(ctl, " Proxy");
#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
    vshPrint(ctl, " Netcf");
#endif
#ifdef WITH_NWFILTER
    vshPrint(ctl, " Nwfilter");
#endif
#ifdef WITH_VIRTUALPORT
    vshPrint(ctl, " VirtualPort");
#endif
    vshPrint(ctl, "\n");

L
Laine Stump 已提交
19832
    vshPrint(ctl, "%s", _(" Storage:"));
19833 19834 19835 19836 19837 19838 19839 19840 19841 19842 19843 19844 19845 19846 19847 19848 19849 19850 19851 19852 19853 19854 19855
#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");
#endif
    vshPrint(ctl, "\n");

L
Laine Stump 已提交
19856
    vshPrint(ctl, "%s", _(" Miscellaneous:"));
19857
#ifdef WITH_SECDRIVER_APPARMOR
19858 19859 19860 19861 19862 19863 19864 19865 19866 19867 19868 19869 19870 19871 19872 19873 19874 19875 19876 19877 19878 19879 19880
    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
    vshPrint(ctl, " DTrace");
#endif
#ifdef USE_READLINE
    vshPrint(ctl, " Readline");
#endif
#ifdef WITH_DRIVER_MODULES
    vshPrint(ctl, " Modular");
#endif
    vshPrint(ctl, "\n");
}

K
Karel Zak 已提交
19881 19882 19883 19884
/*
 * argv[]:  virsh [options] [command]
 *
 */
E
Eric Blake 已提交
19885
static bool
19886
vshParseArgv(vshControl *ctl, int argc, char **argv)
19887
{
19888
    bool help = false;
19889
    int arg, len;
K
Karel Zak 已提交
19890
    struct option opt[] = {
E
Eric Blake 已提交
19891 19892 19893 19894 19895 19896 19897 19898
        {"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'},
19899
        {"escape", required_argument, NULL, 'e'},
E
Eric Blake 已提交
19900
        {NULL, 0, NULL, 0}
19901 19902
    };

19903 19904 19905
    /* 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. */
19906
    while ((arg = getopt_long(argc, argv, "+d:hqtc:vVrl:e:", opt, NULL)) != -1) {
19907
        switch (arg) {
19908
        case 'd':
D
Daniel Veillard 已提交
19909
            if (virStrToLong_i(optarg, NULL, 10, &ctl->debug) < 0) {
L
Laine Stump 已提交
19910
                vshError(ctl, "%s", _("option -d takes a numeric argument"));
D
Daniel Veillard 已提交
19911 19912
                exit(EXIT_FAILURE);
            }
19913 19914
            break;
        case 'h':
19915
            help = true;
19916 19917
            break;
        case 'q':
E
Eric Blake 已提交
19918
            ctl->quiet = true;
19919 19920
            break;
        case 't':
E
Eric Blake 已提交
19921
            ctl->timing = true;
19922 19923 19924 19925
            break;
        case 'c':
            ctl->name = vshStrdup(ctl, optarg);
            break;
E
Eric Blake 已提交
19926 19927 19928 19929 19930 19931
        case 'v':
            if (STRNEQ_NULLABLE(optarg, "long")) {
                puts(VERSION);
                exit(EXIT_SUCCESS);
            }
            /* fall through */
19932 19933 19934
        case 'V':
            vshShowVersion(ctl);
            exit(EXIT_SUCCESS);
19935
        case 'r':
E
Eric Blake 已提交
19936
            ctl->readonly = true;
19937
            break;
19938 19939 19940
        case 'l':
            ctl->logfile = vshStrdup(ctl, optarg);
            break;
19941 19942 19943 19944 19945 19946 19947 19948 19949 19950 19951 19952
        case 'e':
            len = strlen(optarg);

            if ((len == 2 && *optarg == '^') ||
                (len == 1 && *optarg != '^')) {
                ctl->escapeChar = optarg;
            } else {
                vshError(ctl, _("Invalid string '%s' for escape sequence"),
                         optarg);
                exit(EXIT_FAILURE);
            }
            break;
19953
        default:
19954 19955
            vshError(ctl, _("unsupported option '-%c'. See --help."), arg);
            exit(EXIT_FAILURE);
K
Karel Zak 已提交
19956 19957 19958 19959
        }
    }

    if (help) {
19960 19961
        if (optind < argc) {
            vshError(ctl, _("extra argument '%s'. See --help."), argv[optind]);
19962 19963
            exit(EXIT_FAILURE);
        }
19964 19965 19966

        /* list all command */
        vshUsage();
K
Karel Zak 已提交
19967
        exit(EXIT_SUCCESS);
19968 19969
    }

19970
    if (argc > optind) {
K
Karel Zak 已提交
19971
        /* parse command */
E
Eric Blake 已提交
19972
        ctl->imode = false;
19973
        if (argc - optind == 1) {
19974
            vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]);
19975
            return vshCommandStringParse(ctl, argv[optind]);
L
Lai Jiangshan 已提交
19976
        } else {
19977
            return vshCommandArgvParse(ctl, argc - optind, argv + optind);
K
Karel Zak 已提交
19978 19979
        }
    }
E
Eric Blake 已提交
19980
    return true;
K
Karel Zak 已提交
19981 19982
}

19983 19984 19985 19986
int
main(int argc, char **argv)
{
    vshControl _ctl, *ctl = &_ctl;
19987
    char *defaultConn;
E
Eric Blake 已提交
19988
    bool ret = true;
K
Karel Zak 已提交
19989

19990 19991 19992
    memset(ctl, 0, sizeof(vshControl));
    ctl->imode = true;          /* default is interactive mode */
    ctl->log_fd = -1;           /* Initialize log file descriptor */
J
Jiri Denemark 已提交
19993
    ctl->debug = VSH_DEBUG_DEFAULT;
19994 19995
    ctl->escapeChar = CTRL_CLOSE_BRACKET;

19996

19997 19998
    if (!setlocale(LC_ALL, "")) {
        perror("setlocale");
19999
        /* failure to setup locale is not fatal */
20000
    }
20001
    if (!bindtextdomain(PACKAGE, LOCALEDIR)) {
20002
        perror("bindtextdomain");
E
Eric Blake 已提交
20003
        return EXIT_FAILURE;
20004
    }
20005
    if (!textdomain(PACKAGE)) {
20006
        perror("textdomain");
E
Eric Blake 已提交
20007
        return EXIT_FAILURE;
20008 20009
    }

20010 20011 20012 20013 20014
    if (virMutexInit(&ctl->lock) < 0) {
        vshError(ctl, "%s", _("Failed to initialize mutex"));
        return EXIT_FAILURE;
    }

20015 20016 20017 20018 20019
    if (virInitialize() < 0) {
        vshError(ctl, "%s", _("Failed to initialize libvirt"));
        return EXIT_FAILURE;
    }

20020
    if (!(progname = strrchr(argv[0], '/')))
K
Karel Zak 已提交
20021 20022 20023
        progname = argv[0];
    else
        progname++;
20024

20025
    if ((defaultConn = getenv("VIRSH_DEFAULT_CONNECT_URI"))) {
E
Eric Blake 已提交
20026
        ctl->name = vshStrdup(ctl, defaultConn);
20027 20028
    }

D
Daniel P. Berrange 已提交
20029 20030
    if (!vshParseArgv(ctl, argc, argv)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
20031
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
20032
    }
20033

D
Daniel P. Berrange 已提交
20034 20035
    if (!vshInit(ctl)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
20036
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
20037
    }
20038

K
Karel Zak 已提交
20039
    if (!ctl->imode) {
20040
        ret = vshCommandRun(ctl, ctl->cmd);
20041
    } else {
K
Karel Zak 已提交
20042 20043
        /* interactive mode */
        if (!ctl->quiet) {
K
Karel Zak 已提交
20044
            vshPrint(ctl,
20045
                     _("Welcome to %s, the virtualization interactive terminal.\n\n"),
20046
                     progname);
J
Jim Meyering 已提交
20047
            vshPrint(ctl, "%s",
20048
                     _("Type:  'help' for help with commands\n"
20049
                       "       'quit' to quit\n\n"));
K
Karel Zak 已提交
20050
        }
20051 20052 20053 20054 20055 20056

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

K
Karel Zak 已提交
20057
        do {
20058
            const char *prompt = ctl->readonly ? VSH_PROMPT_RO : VSH_PROMPT_RW;
20059
            ctl->cmdstr =
20060
                vshReadline(ctl, prompt);
20061 20062
            if (ctl->cmdstr == NULL)
                break;          /* EOF */
K
Karel Zak 已提交
20063
            if (*ctl->cmdstr) {
20064
#if USE_READLINE
K
Karel Zak 已提交
20065
                add_history(ctl->cmdstr);
20066
#endif
20067
                if (vshCommandStringParse(ctl, ctl->cmdstr))
K
Karel Zak 已提交
20068 20069
                    vshCommandRun(ctl, ctl->cmd);
            }
20070
            VIR_FREE(ctl->cmdstr);
20071
        } while (ctl->imode);
K
Karel Zak 已提交
20072

20073 20074
        if (ctl->cmdstr == NULL)
            fputc('\n', stdout);        /* line break after alone prompt */
K
Karel Zak 已提交
20075
    }
20076

K
Karel Zak 已提交
20077 20078
    vshDeinit(ctl);
    exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
20079
}