virsh.c 542.3 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) */
141
    VSH_OT_ARGV      /* remaining arguments */
K
Karel Zak 已提交
142 143
} vshCmdOptType;

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

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

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

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

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

208 209 210 211 212 213 214
/*
 * Command Usage Flags
 */
enum {
    VSH_CMD_FLAG_NOCONNECT = (1 << 0),  /* no prior connection needed */
};

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

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

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

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

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

273
static const vshCmdGrp cmdGroups[];
K
Karel Zak 已提交
274

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

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

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

E
Eric Blake 已提交
293 294 295
static int vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
    ATTRIBUTE_RETURN_CHECK;
296 297
static int vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
298 299 300
static int vshCommandOptUInt(const vshCmd *cmd, const char *name,
                             unsigned int *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
301 302 303 304 305 306 307 308 309
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;
310 311 312
static int vshCommandOptULongLong(const vshCmd *cmd, const char *name,
                                  unsigned long long *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
E
Eric Blake 已提交
313
static bool vshCommandOptBool(const vshCmd *cmd, const char *name);
314 315
static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd,
                                          const vshCmdOpt *opt);
K
Karel Zak 已提交
316

317 318 319
#define VSH_BYID     (1 << 1)
#define VSH_BYUUID   (1 << 2)
#define VSH_BYNAME   (1 << 3)
320
#define VSH_BYMAC    (1 << 4)
K
Karel Zak 已提交
321

322
static virDomainPtr vshCommandOptDomainBy(vshControl *ctl, const vshCmd *cmd,
323
                                          const char **name, int flag);
K
Karel Zak 已提交
324 325

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

329
static virNetworkPtr vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
330
                                            const char **name, int flag);
331 332

/* default is lookup by Name and UUID */
J
Jim Meyering 已提交
333 334
#define vshCommandOptNetwork(_ctl, _cmd, _name)                    \
    vshCommandOptNetworkBy(_ctl, _cmd, _name,                      \
335 336
                           VSH_BYUUID|VSH_BYNAME)

337
static virNWFilterPtr vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd,
338
                                                  const char **name, int flag);
339 340 341 342 343 344

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

345
static virInterfacePtr vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd,
346
                                                const char *optname,
347
                                                const char **name, int flag);
348 349 350

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

354
static virStoragePoolPtr vshCommandOptPoolBy(vshControl *ctl, const vshCmd *cmd,
355
                            const char *optname, const char **name, int flag);
356 357 358 359 360 361

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

362
static virStorageVolPtr vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd,
363 364
                                           const char *optname,
                                           const char *pooloptname,
365
                                           const char **name, int flag);
366 367

/* default is lookup by Name and UUID */
368
#define vshCommandOptVol(_ctl, _cmd, _optname, _pooloptname, _name)   \
369 370 371
    vshCommandOptVolBy(_ctl, _cmd, _optname, _pooloptname, _name,     \
                           VSH_BYUUID|VSH_BYNAME)

372
static virSecretPtr vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd,
373
                                        const char **name);
374

375
static void vshPrintExtra(vshControl *ctl, const char *format, ...)
376
    ATTRIBUTE_FMT_PRINTF(2, 3);
377
static void vshDebug(vshControl *ctl, int level, const char *format, ...)
378
    ATTRIBUTE_FMT_PRINTF(3, 4);
K
Karel Zak 已提交
379 380

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

383
static int vshDomainState(vshControl *ctl, virDomainPtr dom, int *reason);
K
Karel Zak 已提交
384
static const char *vshDomainStateToString(int state);
385
static const char *vshDomainStateReasonToString(int state, int reason);
386
static const char *vshDomainControlStateToString(int state);
387
static const char *vshDomainVcpuStateToString(int state);
E
Eric Blake 已提交
388
static bool vshConnectionUsability(vshControl *ctl, virConnectPtr conn);
389 390 391
static virTypedParameterPtr vshFindTypedParamByName(const char *name,
                                                    virTypedParameterPtr list,
                                                    int count);
392 393
static char *vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
K
Karel Zak 已提交
394

395 396 397 398
static char *editWriteToTempFile (vshControl *ctl, const char *doc);
static int   editFile (vshControl *ctl, const char *filename);
static char *editReadBackFile (vshControl *ctl, const char *filename);

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
/* 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);

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

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

428
static char *_vshStrdup(vshControl *ctl, const char *s, const char *filename, int line);
429 430
#define vshStrdup(_ctl, _s)    _vshStrdup(_ctl, _s, __FILE__, __LINE__)

431 432
static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate);

E
Eric Blake 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
static void *
_vshMalloc(vshControl *ctl, size_t size, const char *filename, int line)
{
    void *x;

    if ((x = malloc(size)))
        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)
{
    void *x;

    if ((x = calloc(nmemb, size)))
        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)
        return(NULL);
    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
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494

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;

495
  /* User visible sort, so we want locale-specific case comparison.  */
496 497 498
  return strcasecmp(*sa, *sb);
}

499 500 501 502 503 504 505 506 507 508 509 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";
        return ((double)val / (1024.0l * 1024.0l));
    } else if (val < (1024.0l * 1024.0l * 1024.0l * 1024.0l)) {
        *unit = "GB";
        return ((double)val / (1024.0l * 1024.0l * 1024.0l));
    } else {
        *unit = "TB";
        return ((double)val / (1024.0l * 1024.0l * 1024.0l * 1024.0l));
    }
}


J
John Levon 已提交
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
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)
{
546 547 548 549 550 551 552 553
    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)
554
            goto out;
555
    }
J
John Levon 已提交
556 557

    if (last_error->code == VIR_ERR_OK) {
558
        vshError(ctl, "%s", _("unknown error"));
J
John Levon 已提交
559 560 561
        goto out;
    }

562
    vshError(ctl, "%s", last_error->message);
J
John Levon 已提交
563 564 565 566 567 568

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

569 570 571 572 573 574 575 576 577
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;
}

578 579 580 581 582
/*
 * Detection of disconnections and automatic reconnection support
 */
static int disconnected = 0; /* we may have been disconnected */

583 584 585 586 587
/* Gnulib doesn't guarantee SA_SIGINFO support.  */
#ifndef SA_SIGINFO
# define SA_SIGINFO 0
#endif

588 589 590 591 592 593
/*
 * 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
 */
594 595 596 597
static void vshCatchDisconnect(int sig, siginfo_t *siginfo,
                               void *context ATTRIBUTE_UNUSED) {
    if ((sig == SIGPIPE) ||
        (SA_SIGINFO && siginfo->si_signo == SIGPIPE))
598 599 600 601 602 603 604 605 606
        disconnected++;
}

/*
 * vshSetupSignals:
 *
 * Catch SIGPIPE signals which may arise when disconnection
 * from libvirtd occurs
 */
L
Laine Stump 已提交
607
static void
608 609 610 611 612 613 614 615 616 617 618 619 620
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 已提交
621
 * Reconnect after a disconnect from libvirtd
622 623
 *
 */
L
Laine Stump 已提交
624
static void
625 626 627 628 629 630
vshReconnect(vshControl *ctl)
{
    bool connected = false;

    if (ctl->conn != NULL) {
        connected = true;
631
        virConnectClose(ctl->conn);
632
    }
633 634 635 636 637 638

    ctl->conn = virConnectOpenAuth(ctl->name,
                                   virConnectAuthPtrDefault,
                                   ctl->readonly ? VIR_CONNECT_RO : 0);
    if (!ctl->conn)
        vshError(ctl, "%s", _("Failed to reconnect to the hypervisor"));
639
    else if (connected)
640 641
        vshError(ctl, "%s", _("Reconnected to the hypervisor"));
    disconnected = 0;
642
    ctl->useGetInfo = false;
643
    ctl->useSnapshotOld = false;
644
}
645

K
Karel Zak 已提交
646 647 648 649 650 651
/* ---------------
 * Commands
 * ---------------
 */

/*
652
 * "help" command
K
Karel Zak 已提交
653
 */
654
static const vshCmdInfo info_help[] = {
655
    {"help", N_("print help")},
656 657
    {"desc", N_("Prints global help, command specific help, or help for a\n"
                "    group of related commands")},
658

659
    {NULL, NULL}
K
Karel Zak 已提交
660 661
};

662
static const vshCmdOptDef opts_help[] = {
663
    {"command", VSH_OT_DATA, 0, N_("Prints global help, command specific help, or help for a group of related commands")},
664
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
665 666
};

E
Eric Blake 已提交
667
static bool
668
cmdHelp(vshControl *ctl, const vshCmd *cmd)
669
 {
670
    const char *name = NULL;
671

672
    if (vshCommandOptString(cmd, "command", &name) <= 0) {
673
        const vshCmdGrp *grp;
674
        const vshCmdDef *def;
675

676 677 678 679 680 681 682 683 684 685 686 687 688
        vshPrint(ctl, "%s", _("Grouped commands:\n\n"));

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

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

            vshPrint(ctl, "\n");
        }

E
Eric Blake 已提交
689
        return true;
690
    }
691

E
Eric Blake 已提交
692
    if (vshCmddefSearch(name)) {
693
        return vshCmddefHelp(ctl, name);
E
Eric Blake 已提交
694
    } else if (vshCmdGrpSearch(name)) {
695 696 697
        return vshCmdGrpHelp(ctl, name);
    } else {
        vshError(ctl, _("command or command group '%s' doesn't exist"), name);
E
Eric Blake 已提交
698
        return false;
K
Karel Zak 已提交
699 700 701
    }
}

702 703 704
/*
 * "autostart" command
 */
705
static const vshCmdInfo info_autostart[] = {
706
    {"help", N_("autostart a domain")},
707
    {"desc",
708
     N_("Configure a domain to be automatically started at boot.")},
709 710 711
    {NULL, NULL}
};

712
static const vshCmdOptDef opts_autostart[] = {
713 714
    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
715 716 717
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
718
static bool
719
cmdAutostart(vshControl *ctl, const vshCmd *cmd)
720 721
{
    virDomainPtr dom;
722
    const char *name;
723 724
    int autostart;

725
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
726
        return false;
727

J
Jim Meyering 已提交
728
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
729
        return false;
730 731 732 733

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

    if (virDomainSetAutostart(dom, autostart) < 0) {
734
        if (autostart)
735
            vshError(ctl, _("Failed to mark domain %s as autostarted"), name);
736
        else
737
            vshError(ctl, _("Failed to unmark domain %s as autostarted"), name);
738
        virDomainFree(dom);
E
Eric Blake 已提交
739
        return false;
740 741
    }

742
    if (autostart)
743
        vshPrint(ctl, _("Domain %s marked as autostarted\n"), name);
744
    else
745
        vshPrint(ctl, _("Domain %s unmarked as autostarted\n"), name);
746

747
    virDomainFree(dom);
E
Eric Blake 已提交
748
    return true;
749 750
}

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

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

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

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

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

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

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

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

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

803 804
#ifndef WIN32

805
/*
806
 * "console" command
807
 */
808
static const vshCmdInfo info_console[] = {
809
    {"help", N_("connect to the guest console")},
810
    {"desc",
811
     N_("Connect the virtual serial console for the guest")},
812 813 814
    {NULL, NULL}
};

815
static const vshCmdOptDef opts_console[] = {
816
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
817
    {"devname", VSH_OT_STRING, 0, N_("character device name")},
818 819 820
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
821
static bool
822
cmdRunConsole(vshControl *ctl, virDomainPtr dom, const char *name)
823
{
E
Eric Blake 已提交
824
    bool ret = false;
825
    int state;
826

827
    if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
828 829 830 831
        vshError(ctl, "%s", _("Unable to get domain status"));
        goto cleanup;
    }

832
    if (state == VIR_DOMAIN_SHUTOFF) {
833 834 835 836
        vshError(ctl, "%s", _("The domain is not running"));
        goto cleanup;
    }

837
    vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
838 839
    vshPrintExtra(ctl, _("Escape character is %s\n"), ctl->escapeChar);
    if (vshRunConsole(dom, name, ctl->escapeChar) == 0)
E
Eric Blake 已提交
840
        ret = true;
841 842

 cleanup:
843

844 845 846
    return ret;
}

E
Eric Blake 已提交
847
static bool
848 849 850
cmdConsole(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
851
    bool ret = false;
852
    const char *name = NULL;
853

854
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
855
        return false;
856 857

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

860 861 862 863
    if (vshCommandOptString(cmd, "devname", &name) < 0) {
        vshError(ctl, "%s", _("Invalid devname"));
        goto cleanup;
    }
864

865
    ret = cmdRunConsole(ctl, dom, name);
866

867
cleanup:
868 869 870 871
    virDomainFree(dom);
    return ret;
}

872 873 874
#endif /* WIN32 */


K
Karel Zak 已提交
875 876 877
/*
 * "list" command
 */
878
static const vshCmdInfo info_list[] = {
879 880
    {"help", N_("list domains")},
    {"desc", N_("Returns list of domains.")},
881
    {NULL, NULL}
K
Karel Zak 已提交
882 883
};

884
static const vshCmdOptDef opts_list[] = {
885 886
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive domains")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active domains")},
E
Eric Blake 已提交
887 888
    {"managed-save", VSH_OT_BOOL, 0,
     N_("mark domains with managed save state")},
889 890 891
    {NULL, 0, 0, NULL}
};

K
Karel Zak 已提交
892

E
Eric Blake 已提交
893
static bool
894
cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
895
{
896 897 898 899
    int inactive = vshCommandOptBool(cmd, "inactive");
    int all = vshCommandOptBool(cmd, "all");
    int active = !inactive || all ? 1 : 0;
    int *ids = NULL, maxid = 0, i;
900
    char **names = NULL;
901
    int maxname = 0;
E
Eric Blake 已提交
902 903 904
    bool managed = vshCommandOptBool(cmd, "managed-save");
    int state;

905
    inactive |= all;
K
Karel Zak 已提交
906

907
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
908
        return false;
909

910
    if (active) {
911 912
        maxid = virConnectNumOfDomains(ctl->conn);
        if (maxid < 0) {
913
            vshError(ctl, "%s", _("Failed to list active domains"));
E
Eric Blake 已提交
914
            return false;
915 916 917 918 919
        }
        if (maxid) {
            ids = vshMalloc(ctl, sizeof(int) * maxid);

            if ((maxid = virConnectListDomains(ctl->conn, &ids[0], maxid)) < 0) {
920
                vshError(ctl, "%s", _("Failed to list active domains"));
921
                VIR_FREE(ids);
E
Eric Blake 已提交
922
                return false;
923 924
            }

925
            qsort(&ids[0], maxid, sizeof(int), idsorter);
926
        }
927 928
    }
    if (inactive) {
929 930
        maxname = virConnectNumOfDefinedDomains(ctl->conn);
        if (maxname < 0) {
931
            vshError(ctl, "%s", _("Failed to list inactive domains"));
932
            VIR_FREE(ids);
E
Eric Blake 已提交
933
            return false;
934
        }
935 936 937 938
        if (maxname) {
            names = vshMalloc(ctl, sizeof(char *) * maxname);

            if ((maxname = virConnectListDefinedDomains(ctl->conn, names, maxname)) < 0) {
939
                vshError(ctl, "%s", _("Failed to list inactive domains"));
940 941
                VIR_FREE(ids);
                VIR_FREE(names);
E
Eric Blake 已提交
942
                return false;
943
            }
944

945
            qsort(&names[0], maxname, sizeof(char*), namesorter);
946
        }
947
    }
948 949
    vshPrintExtra(ctl, " %-5s %-30s %s\n", _("Id"), _("Name"), _("State"));
    vshPrintExtra(ctl, "----------------------------------------------------\n");
950 951

    for (i = 0; i < maxid; i++) {
K
Karel Zak 已提交
952
        virDomainPtr dom = virDomainLookupByID(ctl->conn, ids[i]);
953 954

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

958
        vshPrint(ctl, " %-5d %-30s %s\n",
959 960
                 virDomainGetID(dom),
                 virDomainGetName(dom),
E
Eric Blake 已提交
961
                 _(vshDomainStateToString(vshDomainState(ctl, dom, NULL))));
962
        virDomainFree(dom);
K
Karel Zak 已提交
963
    }
964 965 966 967
    for (i = 0; i < maxname; i++) {
        virDomainPtr dom = virDomainLookupByName(ctl->conn, names[i]);

        /* this kind of work with domains is not atomic operation */
968
        if (!dom) {
969
            VIR_FREE(names[i]);
970
            continue;
971
        }
972

E
Eric Blake 已提交
973 974 975 976 977
        state = vshDomainState(ctl, dom, NULL);
        if (managed && state == VIR_DOMAIN_SHUTOFF &&
            virDomainHasManagedSaveImage(dom, 0) > 0)
            state = -2;

978
        vshPrint(ctl, " %-5s %-30s %s\n",
979 980
                 "-",
                 names[i],
E
Eric Blake 已提交
981
                 state == -2 ? _("saved") : _(vshDomainStateToString(state)));
982

983
        virDomainFree(dom);
984
        VIR_FREE(names[i]);
985
    }
986 987
    VIR_FREE(ids);
    VIR_FREE(names);
E
Eric Blake 已提交
988
    return true;
K
Karel Zak 已提交
989 990 991
}

/*
K
Karel Zak 已提交
992
 * "domstate" command
K
Karel Zak 已提交
993
 */
994
static const vshCmdInfo info_domstate[] = {
995 996
    {"help", N_("domain state")},
    {"desc", N_("Returns state about a domain.")},
997
    {NULL, NULL}
K
Karel Zak 已提交
998 999
};

1000
static const vshCmdOptDef opts_domstate[] = {
1001
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
1002
    {"reason", VSH_OT_BOOL, 0, N_("also print reason for the state")},
1003
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
1004 1005
};

E
Eric Blake 已提交
1006
static bool
1007
cmdDomstate(vshControl *ctl, const vshCmd *cmd)
1008
{
K
Karel Zak 已提交
1009
    virDomainPtr dom;
E
Eric Blake 已提交
1010
    bool ret = true;
1011 1012
    int showReason = vshCommandOptBool(cmd, "reason");
    int state, reason;
1013

1014
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1015
        return false;
1016

J
Jim Meyering 已提交
1017
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
1018
        return false;
1019

1020
    if ((state = vshDomainState(ctl, dom, &reason)) < 0) {
E
Eric Blake 已提交
1021
        ret = false;
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
        goto cleanup;
    }

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

1034
cleanup:
1035 1036 1037 1038
    virDomainFree(dom);
    return ret;
}

1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 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 1082 1083 1084 1085
/*
 * "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;
}

1086 1087
/* "domblkstat" command
 */
1088
static const vshCmdInfo info_domblkstat[] = {
1089
    {"help", N_("get device block stats for a domain")},
1090 1091
    {"desc", N_("Get device block stats for a running domain. See man page or "
                "use --human for explanation of fields")},
1092 1093 1094
    {NULL,NULL}
};

1095
static const vshCmdOptDef opts_domblkstat[] = {
1096 1097
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
    {"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",
1112
      N_("number of read operations:") }, /* 0 */
1113
    { VIR_DOMAIN_BLOCK_STATS_READ_BYTES,        "rd_bytes",
1114
      N_("number of bytes read:") }, /* 1 */
1115
    { VIR_DOMAIN_BLOCK_STATS_WRITE_REQ,         "wr_req",
1116
      N_("number of write operations:") }, /* 2 */
1117
    { VIR_DOMAIN_BLOCK_STATS_WRITE_BYTES,       "wr_bytes",
1118
      N_("number of bytes written:") }, /* 3 */
1119
    { VIR_DOMAIN_BLOCK_STATS_ERRS,              "errs",
1120
      N_("error count:") }, /* 4 */
1121
    { VIR_DOMAIN_BLOCK_STATS_FLUSH_REQ,         NULL,
1122
      N_("number of flush operations:") }, /* 5 */
1123
    { VIR_DOMAIN_BLOCK_STATS_READ_TOTAL_TIMES,  NULL,
1124
      N_("total duration of reads (ns):") }, /* 6 */
1125
    { VIR_DOMAIN_BLOCK_STATS_WRITE_TOTAL_TIMES, NULL,
1126
      N_("total duration of writes (ns):") }, /* 7 */
1127 1128 1129 1130 1131 1132 1133
    { 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)                                     \
1134 1135
        vshPrint(ctl, "%s %-*s %lld\n", device,         \
                 human ? 31 : 0,                        \
1136 1137 1138
                 human ? _(domblkstat_output[ID].human) \
                 : domblkstat_output[ID].legacy,        \
                 VALUE);
1139

E
Eric Blake 已提交
1140
static bool
1141
cmdDomblkstat (vshControl *ctl, const vshCmd *cmd)
1142 1143
{
    virDomainPtr dom;
1144
    const char *name = NULL, *device = NULL;
1145
    struct _virDomainBlockStats stats;
1146
    virTypedParameterPtr params = NULL;
1147 1148 1149
    virTypedParameterPtr par = NULL;
    char *value = NULL;
    const char *field = NULL;
1150
    int rc, nparams = 0;
1151
    int i = 0;
1152
    bool ret = false;
1153
    bool human = vshCommandOptBool(cmd, "human"); /* human readable output */
1154

1155
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1156
        return false;
1157

1158
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
1159
        return false;
1160

1161
    if (vshCommandOptString(cmd, "device", &device) <= 0)
1162
        goto cleanup;
1163

1164 1165 1166 1167 1168 1169 1170
    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) {
1171 1172
        /* try older API if newer is not supported */
        if (last_error->code != VIR_ERR_NO_SUPPORT)
1173 1174
            goto cleanup;

1175 1176
        virFreeError(last_error);
        last_error = NULL;
1177

1178 1179 1180 1181 1182 1183
        if (virDomainBlockStats(dom, device, &stats,
                                sizeof stats) == -1) {
            vshError(ctl, _("Failed to get block stats %s %s"),
                     name, device);
            goto cleanup;
        }
1184

1185 1186 1187 1188
        /* human friendly output */
        if (human) {
            vshPrint(ctl, N_("Device: %s\n"), device);
            device = "";
1189
        }
1190 1191 1192 1193 1194 1195

        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);
1196
    } else {
1197
        params = vshCalloc(ctl, nparams, sizeof(*params));
1198

1199 1200 1201 1202
        if (virDomainBlockStatsFlags (dom, device, params, &nparams, 0) < 0) {
            vshError(ctl, _("Failed to get block stats %s %s"), name, device);
            goto cleanup;
        }
1203

1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
        /* 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;

1217
            value = vshGetTypedParamValue(ctl, par);
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232

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

1233 1234
            vshPrint(ctl, "%s %-*s %s\n", device,
                     human ? 31 : 0, field, value);
1235 1236 1237 1238 1239

            VIR_FREE(value);
        }

        /* go through the fields again, for remaining fields */
1240
        for (i = 0; i < nparams; i++) {
1241 1242
            if (!*params[i].field)
                continue;
1243

1244
            value = vshGetTypedParamValue(ctl, params+i);
1245 1246
            vshPrint(ctl, "%s %s %s\n", device, params[i].field, value);
            VIR_FREE(value);
1247 1248 1249 1250 1251 1252 1253
        }
    }

    ret = true;

cleanup:
    VIR_FREE(params);
1254
    virDomainFree(dom);
1255
    return ret;
1256
}
1257
#undef DOMBLKSTAT_LEGACY_PRINT
1258 1259 1260

/* "domifstat" command
 */
1261
static const vshCmdInfo info_domifstat[] = {
1262 1263
    {"help", N_("get network interface stats for a domain")},
    {"desc", N_("Get network interface stats for a running domain.")},
1264 1265 1266
    {NULL,NULL}
};

1267
static const vshCmdOptDef opts_domifstat[] = {
1268 1269
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device")},
1270 1271 1272
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1273
static bool
1274
cmdDomIfstat (vshControl *ctl, const vshCmd *cmd)
1275 1276
{
    virDomainPtr dom;
1277
    const char *name = NULL, *device = NULL;
1278 1279
    struct _virDomainInterfaceStats stats;

1280
    if (!vshConnectionUsability (ctl, ctl->conn))
E
Eric Blake 已提交
1281
        return false;
1282

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

1286
    if (vshCommandOptString (cmd, "interface", &device) <= 0) {
L
Laine Stump 已提交
1287
        virDomainFree(dom);
E
Eric Blake 已提交
1288
        return false;
L
Laine Stump 已提交
1289
    }
1290 1291

    if (virDomainInterfaceStats (dom, device, &stats, sizeof stats) == -1) {
1292
        vshError(ctl, _("Failed to get interface stats %s %s"), name, device);
1293
        virDomainFree(dom);
E
Eric Blake 已提交
1294
        return false;
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
    }

    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 已提交
1322
    return true;
1323 1324
}

1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
/* "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")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist interface state")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomIfSetLink (vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
1345
    const char *iface;
1346
    const char *state;
1347
    const char *value;
1348
    const char *desc;
1349 1350 1351
    unsigned char macaddr[VIR_MAC_BUFLEN];
    const char *element;
    const char *attr;
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367
    bool persistent;
    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;

1368
    if (vshCommandOptString(cmd, "interface", &iface) <= 0)
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 1395 1396
        goto cleanup;

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

    persistent = vshCommandOptBool(cmd, "persistent");

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

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

    if (persistent)
        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 */
1397
    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410
    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;
    }

1411 1412 1413 1414 1415 1416 1417 1418
    if (virParseMacAddr(iface, macaddr) == 0) {
        element = "mac";
        attr = "address";
    } else {
        element = "target";
        attr = "dev";
    }

1419 1420 1421 1422 1423 1424
    /* 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 &&
1425 1426
                xmlStrEqual(cur->name, BAD_CAST element)) {
                value = virXMLPropString(cur, attr);
1427

1428 1429
                if (STRCASEEQ(value, iface)) {
                    VIR_FREE(value);
1430 1431
                    goto hit;
                }
1432
                VIR_FREE(value);
1433 1434 1435 1436 1437
            }
            cur = cur->next;
        }
    }

1438
    vshError(ctl, _("interface (%s: %s) not found"), element, iface);
1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519
    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)")},
    {"persistent", VSH_OT_BOOL, 0, N_("Get persistent interface state")},
    {NULL, 0, 0, NULL}
};

static bool
cmdDomIfGetLink (vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
1520
    const char *iface = NULL;
1521 1522
    int flags = 0;
    char *state = NULL;
1523 1524 1525 1526
    char *value = NULL;
    unsigned char macaddr[VIR_MAC_BUFLEN];
    const char *element;
    const char *attr;
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
    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;

1541
    if (vshCommandOptString (cmd, "interface", &iface) <= 0) {
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554
        virDomainFree(dom);
        return false;
    }

    if (vshCommandOptBool(cmd, "persistent"))
        flags = VIR_DOMAIN_XML_INACTIVE;

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

1555
    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
    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;
    }

1569 1570 1571 1572 1573 1574 1575 1576
    if (virParseMacAddr(iface, macaddr) == 0) {
        element = "mac";
        attr = "address";
    } else {
        element = "target";
        attr = "dev";
    }

1577 1578 1579 1580 1581 1582
    /* 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 &&
1583
                xmlStrEqual(cur->name, BAD_CAST element)) {
1584

1585
                value = virXMLPropString(cur, attr);
1586

1587 1588
                if (STRCASEEQ(value, iface)) {
                    VIR_FREE(value);
1589 1590
                    goto hit;
                }
1591
                VIR_FREE(value);
1592 1593 1594 1595 1596
            }
            cur = cur->next;
        }
    }

1597
    vshError(ctl, _("Interface (%s: %s) not found."), element, iface);
1598 1599 1600 1601 1602 1603 1604 1605 1606
    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");
1607
            vshPrint(ctl, "%s %s", iface, state);
1608 1609 1610 1611 1612 1613 1614 1615
            VIR_FREE(state);

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

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

1618
    ret = true;
1619 1620 1621 1622 1623 1624 1625 1626 1627 1628
cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
    if (dom)
        virDomainFree(dom);

    return ret;
}

1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 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 1822 1823
/* "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;
}

1824 1825 1826
/*
 * "dommemstats" command
 */
1827
static const vshCmdInfo info_dommemstat[] = {
1828
    {"help", N_("get memory statistics for a domain")},
Y
Yuri Chornoivan 已提交
1829
    {"desc", N_("Get memory statistics for a running domain.")},
1830 1831 1832
    {NULL,NULL}
};

1833
static const vshCmdOptDef opts_dommemstat[] = {
1834
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
1835 1836 1837
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1838
static bool
1839
cmdDomMemStat(vshControl *ctl, const vshCmd *cmd)
1840 1841
{
    virDomainPtr dom;
1842
    const char *name;
1843 1844 1845
    struct _virDomainMemoryStat stats[VIR_DOMAIN_MEMORY_STAT_NR];
    unsigned int nr_stats, i;

1846
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1847
        return false;
1848 1849

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
1850
        return false;
1851 1852 1853 1854 1855

    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 已提交
1856
        return false;
1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
    }

    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);
1872 1873
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON)
            vshPrint (ctl, "actual %llu\n", stats[i].val);
1874 1875 1876
    }

    virDomainFree(dom);
E
Eric Blake 已提交
1877
    return true;
1878 1879
}

1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894
/*
 * "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 已提交
1895
static bool
1896 1897 1898 1899
cmdDomblkinfo(vshControl *ctl, const vshCmd *cmd)
{
    virDomainBlockInfo info;
    virDomainPtr dom;
E
Eric Blake 已提交
1900
    bool ret = true;
1901
    const char *device = NULL;
1902

1903
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1904
        return false;
1905 1906

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

1909
    if (vshCommandOptString (cmd, "device", &device) <= 0) {
1910
        virDomainFree(dom);
E
Eric Blake 已提交
1911
        return false;
1912 1913 1914 1915
    }

    if (virDomainGetBlockInfo(dom, device, &info, 0) < 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
1916
        return false;
1917 1918 1919 1920 1921 1922 1923 1924 1925 1926
    }

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

1927 1928 1929 1930 1931
/*
 * "domblklist" command
 */
static const vshCmdInfo info_domblklist[] = {
    {"help", N_("list all domain blocks")},
1932
    {"desc", N_("Get the summary of block devices for a domain.")},
1933 1934 1935 1936 1937 1938 1939
    {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")},
1940 1941
    {"details", VSH_OT_BOOL, 0,
     N_("additionally display the type and device value")},
1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956
    {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;
1957
    bool details = false;
1958 1959 1960 1961

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

1962 1963
    details = vshCommandOptBool(cmd, "details");

1964 1965 1966 1967 1968 1969 1970 1971 1972 1973
    if (!vshConnectionUsability(ctl, ctl->conn))
        return false;

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

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

1974
    xmldoc = virXMLParseStringCtxt(xml, _("(domain_definition)"), &ctxt);
1975 1976 1977 1978 1979 1980 1981
    if (!xmldoc)
        goto cleanup;

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

1982 1983 1984 1985 1986 1987
    if (details)
        vshPrint(ctl, "%-10s %-10s %-10s %s\n", _("Type"),
                 _("Device"), _("Target"), _("Source"));
    else
        vshPrint(ctl, "%-10s %s\n", _("Target"), _("Source"));

1988 1989 1990
    vshPrint(ctl, "------------------------------------------------\n");

    for (i = 0; i < ndisks; i++) {
1991 1992
        char *type;
        char *device;
1993 1994 1995 1996
        char *target;
        char *source;

        ctxt->node = disks[i];
1997 1998 1999 2000 2001 2002

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

2003 2004 2005 2006 2007 2008 2009 2010 2011
        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);
2012 2013 2014 2015 2016 2017 2018 2019 2020
        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 : "-");
        }

2021 2022 2023 2024
        VIR_FREE(target);
        VIR_FREE(source);
    }

A
Alex Jia 已提交
2025
    ret = true;
2026 2027 2028 2029

cleanup:
    VIR_FREE(disks);
    virDomainFree(dom);
2030 2031 2032
    VIR_FREE(xml);
    xmlFreeDoc(xmldoc);
    xmlXPathFreeContext(ctxt);
2033 2034 2035
    return ret;
}

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
/*
 * "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 已提交
2086 2087
    vshPrint(ctl, "%-10s %-10s %-10s %-11s %s\n", _("Interface"), _("Type"),
             _("Source"), _("Model"), _("MAC"));
2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108
    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 已提交
2109
        vshPrint(ctl, "%-10s %-10s %-10s %-11s %-10s\n",
2110
                 target ? target : "-",
T
Taku Izumi 已提交
2111 2112
                 type,
                 source ? source : "-",
2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133
                 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;
}

2134 2135 2136
/*
 * "suspend" command
 */
2137
static const vshCmdInfo info_suspend[] = {
2138 2139
    {"help", N_("suspend a domain")},
    {"desc", N_("Suspend a running domain.")},
2140
    {NULL, NULL}
2141 2142
};

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

E
Eric Blake 已提交
2148
static bool
2149
cmdSuspend(vshControl *ctl, const vshCmd *cmd)
2150
{
2151
    virDomainPtr dom;
2152
    const char *name;
E
Eric Blake 已提交
2153
    bool ret = true;
2154

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

J
Jim Meyering 已提交
2158
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2159
        return false;
2160 2161

    if (virDomainSuspend(dom) == 0) {
2162
        vshPrint(ctl, _("Domain %s suspended\n"), name);
2163
    } else {
2164
        vshError(ctl, _("Failed to suspend domain %s"), name);
E
Eric Blake 已提交
2165
        ret = false;
2166
    }
2167

2168 2169 2170 2171
    virDomainFree(dom);
    return ret;
}

2172 2173 2174
/*
 * "create" command
 */
2175
static const vshCmdInfo info_create[] = {
2176 2177
    {"help", N_("create a domain from an XML file")},
    {"desc", N_("Create a domain.")},
2178 2179 2180
    {NULL, NULL}
};

2181
static const vshCmdOptDef opts_create[] = {
2182
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
2183
#ifndef WIN32
2184
    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
2185
#endif
2186
    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
2187
    {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroy the guest when virsh disconnects")},
2188 2189 2190
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2191
static bool
2192
cmdCreate(vshControl *ctl, const vshCmd *cmd)
2193 2194
{
    virDomainPtr dom;
2195
    const char *from = NULL;
E
Eric Blake 已提交
2196
    bool ret = true;
2197
    char *buffer;
2198
#ifndef WIN32
2199
    int console = vshCommandOptBool(cmd, "console");
2200
#endif
2201
    unsigned int flags = VIR_DOMAIN_NONE;
2202

2203
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2204
        return false;
2205

2206
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
2207
        return false;
2208

2209
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
2210
        return false;
2211

2212 2213
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_START_PAUSED;
2214 2215
    if (vshCommandOptBool(cmd, "autodestroy"))
        flags |= VIR_DOMAIN_START_AUTODESTROY;
2216 2217

    dom = virDomainCreateXML(ctl->conn, buffer, flags);
2218
    VIR_FREE(buffer);
2219

2220
    if (dom != NULL) {
2221
        vshPrint(ctl, _("Domain %s created from %s\n"),
2222
                 virDomainGetName(dom), from);
2223
#ifndef WIN32
2224
        if (console)
2225
            cmdRunConsole(ctl, dom, NULL);
2226
#endif
2227
        virDomainFree(dom);
2228
    } else {
2229
        vshError(ctl, _("Failed to create domain from %s"), from);
E
Eric Blake 已提交
2230
        ret = false;
2231 2232 2233 2234
    }
    return ret;
}

2235 2236 2237
/*
 * "define" command
 */
2238
static const vshCmdInfo info_define[] = {
2239 2240
    {"help", N_("define (but don't start) a domain from an XML file")},
    {"desc", N_("Define a domain.")},
2241 2242 2243
    {NULL, NULL}
};

2244
static const vshCmdOptDef opts_define[] = {
2245
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
2246 2247 2248
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2249
static bool
2250
cmdDefine(vshControl *ctl, const vshCmd *cmd)
2251 2252
{
    virDomainPtr dom;
2253
    const char *from = NULL;
E
Eric Blake 已提交
2254
    bool ret = true;
2255
    char *buffer;
2256

2257
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2258
        return false;
2259

2260
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
2261
        return false;
2262

2263
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
2264
        return false;
2265 2266

    dom = virDomainDefineXML(ctl->conn, buffer);
2267
    VIR_FREE(buffer);
2268

2269
    if (dom != NULL) {
2270
        vshPrint(ctl, _("Domain %s defined from %s\n"),
2271
                 virDomainGetName(dom), from);
2272
        virDomainFree(dom);
2273
    } else {
2274
        vshError(ctl, _("Failed to define domain from %s"), from);
E
Eric Blake 已提交
2275
        ret = false;
2276 2277 2278 2279 2280 2281 2282
    }
    return ret;
}

/*
 * "undefine" command
 */
2283
static const vshCmdInfo info_undefine[] = {
2284 2285 2286
    {"help", N_("undefine a domain")},
    {"desc",
     N_("Undefine an inactive domain, or convert persistent to transient.")},
2287 2288 2289
    {NULL, NULL}
};

2290
static const vshCmdOptDef opts_undefine[] = {
2291
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
2292
    {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
2293 2294 2295 2296 2297 2298 2299
    {"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")},
2300 2301
    {"snapshots-metadata", VSH_OT_BOOL, 0,
     N_("remove all domain snapshot metadata, if inactive")},
2302 2303 2304
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2305
static bool
2306
cmdUndefine(vshControl *ctl, const vshCmd *cmd)
2307 2308
{
    virDomainPtr dom;
2309
    bool ret = false;
2310
    const char *name = NULL;
2311
    /* Flags to attempt.  */
E
Eric Blake 已提交
2312
    unsigned int flags = 0;
2313 2314 2315
    /* User-requested actions.  */
    bool managed_save = vshCommandOptBool(cmd, "managed-save");
    bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
2316 2317 2318
    bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
    bool remove_storage = false;
    bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
2319
    /* Positive if these items exist.  */
2320
    int has_managed_save = 0;
2321 2322 2323 2324 2325
    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;
2326
    int rc = -1;
2327
    int running;
2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344
    /* 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;
2345

2346
    if (managed_save) {
2347
        flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
2348 2349 2350 2351 2352 2353
        managed_save_safe = true;
    }
    if (snapshots_metadata) {
        flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
        snapshots_safe = true;
    }
2354

2355
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2356
        return false;
2357

2358
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2359
        return false;
2360

2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371
    /* 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;
   }

2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390
    /* 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;
            }
2391 2392
            virFreeError(last_error);
            last_error = NULL;
2393
            has_managed_save = 0;
2394 2395
        }

2396 2397
        has_snapshots = virDomainSnapshotNum(dom, 0);
        if (has_snapshots < 0) {
2398 2399
            if (last_error->code != VIR_ERR_NO_SUPPORT) {
                virshReportError(ctl);
2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411
                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.  */
2412 2413
                virFreeError(last_error);
                last_error = NULL;
2414 2415 2416 2417 2418
                has_snapshots_metadata = has_snapshots;
            } else {
                /* The server knew the new flag, all aspects of
                 * undefineFlags are safe.  */
                managed_save_safe = snapshots_safe = true;
2419
            }
2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432
        }
    }
    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;
    }
2433

2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446
    /* 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;
        }
    }

2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460
    /* 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;
    }
2461

2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473
    /* 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;
2474 2475 2476
        }
    }

2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490
    /* 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:
2491
    if (rc == 0) {
2492
        vshPrint(ctl, _("Domain %s has been undefined\n"), name);
2493
        ret = true;
2494
    } else {
2495
        vshError(ctl, _("Failed to undefine domain %s"), name);
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 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 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616
        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;
2617 2618
    }

2619
cleanup:
2620 2621 2622 2623 2624 2625 2626 2627
    VIR_FREE(source);
    VIR_FREE(target);
    VIR_FREE(volumes);
    VIR_FREE(volume_tokens);
    VIR_FREE(def);
    VIR_FREE(vol_nodes);
    xmlFreeDoc(doc);
    xmlXPathFreeContext(ctxt);
2628
    virDomainFree(dom);
2629 2630 2631 2632 2633 2634 2635
    return ret;
}


/*
 * "start" command
 */
2636
static const vshCmdInfo info_start[] = {
2637
    {"help", N_("start a (previously defined) inactive domain")},
2638 2639 2640
    {"desc", N_("Start a domain, either from the last managedsave\n"
                "    state, or via a fresh boot if no managedsave state\n"
                "    is present.")},
2641 2642 2643
    {NULL, NULL}
};

2644
static const vshCmdOptDef opts_start[] = {
2645
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive domain")},
2646
#ifndef WIN32
2647
    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
2648
#endif
E
Eric Blake 已提交
2649
    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
2650 2651
    {"autodestroy", VSH_OT_BOOL, 0,
     N_("automatically destroy the guest when virsh disconnects")},
2652 2653
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when loading")},
2654 2655
    {"force-boot", VSH_OT_BOOL, 0,
     N_("force fresh boot by discarding any managed save")},
2656 2657 2658
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2659
static bool
2660
cmdStart(vshControl *ctl, const vshCmd *cmd)
2661 2662
{
    virDomainPtr dom;
2663
    bool ret = false;
2664
#ifndef WIN32
2665
    int console = vshCommandOptBool(cmd, "console");
2666
#endif
E
Eric Blake 已提交
2667
    unsigned int flags = VIR_DOMAIN_NONE;
2668
    int rc;
2669

2670
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2671
        return false;
2672

2673 2674
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
                                      VSH_BYNAME | VSH_BYUUID)))
E
Eric Blake 已提交
2675
        return false;
2676 2677

    if (virDomainGetID(dom) != (unsigned int)-1) {
2678
        vshError(ctl, "%s", _("Domain is already active"));
2679
        virDomainFree(dom);
E
Eric Blake 已提交
2680
        return false;
2681 2682
    }

E
Eric Blake 已提交
2683 2684
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_START_PAUSED;
2685 2686
    if (vshCommandOptBool(cmd, "autodestroy"))
        flags |= VIR_DOMAIN_START_AUTODESTROY;
2687 2688
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_START_BYPASS_CACHE;
2689 2690
    if (vshCommandOptBool(cmd, "force-boot"))
        flags |= VIR_DOMAIN_START_FORCE_BOOT;
E
Eric Blake 已提交
2691

2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716
    /* 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 已提交
2717 2718
    /* Prefer older API unless we have to pass a flag.  */
    if ((flags ? virDomainCreateWithFlags(dom, flags)
2719
         : virDomainCreate(dom)) < 0) {
2720
        vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
2721
        goto cleanup;
2722
    }
2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734

started:
    vshPrint(ctl, _("Domain %s started\n"),
             virDomainGetName(dom));
#ifndef WIN32
    if (console && !cmdRunConsole(ctl, dom, NULL))
        goto cleanup;
#endif

    ret = true;

cleanup:
2735
    virDomainFree(dom);
2736 2737 2738
    return ret;
}

2739 2740 2741
/*
 * "save" command
 */
2742
static const vshCmdInfo info_save[] = {
2743
    {"help", N_("save a domain state to a file")},
E
Eric Blake 已提交
2744
    {"desc", N_("Save the RAM state of a running domain.")},
2745
    {NULL, NULL}
2746 2747
};

2748
static const vshCmdOptDef opts_save[] = {
2749
    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
2750 2751
    {"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")},
2752 2753
    {"xml", VSH_OT_STRING, 0,
     N_("filename containing updated XML for the target")},
2754 2755
    {"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")},
2756
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
2757
    {NULL, 0, 0, NULL}
2758 2759
};

2760 2761
static void
doSave(void *opaque)
2762
{
2763 2764 2765 2766 2767
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
    char ret = '1';
    virDomainPtr dom = NULL;
2768 2769
    const char *name = NULL;
    const char *to = NULL;
E
Eric Blake 已提交
2770
    unsigned int flags = 0;
2771 2772
    const char *xmlfile = NULL;
    char *xml = NULL;
2773 2774 2775 2776 2777 2778
    sigset_t sigmask, oldsigmask;

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

2780
    if (!vshConnectionUsability(ctl, ctl->conn))
2781
        goto out;
2782

2783
    if (vshCommandOptString(cmd, "file", &to) <= 0)
2784
        goto out;
2785

2786 2787
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
2788 2789 2790 2791
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
2792

2793 2794
    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
        vshError(ctl, "%s", _("malformed xml argument"));
2795
        goto out;
2796 2797
    }

J
Jim Meyering 已提交
2798
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
2799
        goto out;
2800

2801 2802
    if (xmlfile &&
        virFileReadAll(xmlfile, 8192, &xml) < 0)
2803
        goto out;
2804 2805 2806

    if (((flags || xml)
         ? virDomainSaveFlags(dom, to, xml, flags)
2807
         : virDomainSave(dom, to)) < 0) {
2808
        vshError(ctl, _("Failed to save domain %s to %s"), name, to);
2809
        goto out;
2810
    }
2811

2812
    ret = '0';
2813

2814 2815 2816 2817
out:
    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
out_sig:
    if (dom) virDomainFree (dom);
2818
    VIR_FREE(xml);
2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865
    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);
2866 2867 2868
    return ret;
}

2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889
/*
 * "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 已提交
2890
    unsigned int flags = 0;
2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926
    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")},
2927 2928
    {"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")},
2929 2930 2931 2932 2933 2934 2935 2936 2937 2938
    {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;
2939 2940 2941 2942 2943 2944
    unsigned int flags = 0;

    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959

    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;

2960
    if (virDomainSaveImageDefineXML(ctl->conn, file, xml, flags) < 0) {
2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983
        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")},
2984 2985
    {"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")},
2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996
    {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;
2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012
    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;
    }
3013 3014 3015 3016 3017 3018 3019 3020

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

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

    /* Get the XML configuration of the saved image.  */
3021
    doc = virDomainSaveImageGetXMLDesc(ctl->conn, file, getxml_flags);
3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038
    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;

3039 3040 3041
    /* 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) {
3042 3043 3044 3045 3046 3047 3048
        vshPrint(ctl, _("Saved image %s XML configuration not changed.\n"),
                 file);
        ret = true;
        goto cleanup;
    }

    /* Everything checks out, so redefine the xml.  */
3049 3050
    if (virDomainSaveImageDefineXML(ctl->conn, file, doc_edited,
                                    define_flags) < 0) {
3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067
        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;
}

3068 3069 3070 3071 3072
/*
 * "managedsave" command
 */
static const vshCmdInfo info_managedsave[] = {
    {"help", N_("managed save of a domain state")},
3073 3074 3075 3076
    {"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.")},
3077 3078 3079 3080
    {NULL, NULL}
};

static const vshCmdOptDef opts_managedsave[] = {
3081
    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
3082
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3083 3084
    {"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")},
3085
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
3086 3087 3088
    {NULL, 0, 0, NULL}
};

3089 3090
static void
doManagedsave(void *opaque)
3091
{
3092 3093 3094 3095 3096
    char ret = '1';
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
    virDomainPtr dom = NULL;
3097
    const char *name;
E
Eric Blake 已提交
3098
    unsigned int flags = 0;
3099 3100 3101 3102 3103 3104
    sigset_t sigmask, oldsigmask;

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

3106
    if (!vshConnectionUsability(ctl, ctl->conn))
3107
        goto out;
3108

3109 3110
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
3111 3112 3113 3114
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
3115

3116
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
3117
        goto out;
3118

3119
    if (virDomainManagedSave(dom, flags) < 0) {
3120
        vshError(ctl, _("Failed to save domain %s state"), name);
3121
        goto out;
3122 3123
    }

3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169
    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);
3170 3171

cleanup:
3172
    virDomainFree(dom);
3173 3174
    VIR_FORCE_CLOSE(p[0]);
    VIR_FORCE_CLOSE(p[1]);
3175 3176 3177
    return ret;
}

3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191
/*
 * "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 已提交
3192
static bool
3193 3194 3195
cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
3196
    const char *name;
E
Eric Blake 已提交
3197
    bool ret = false;
3198 3199
    int hassave;

3200
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3201
        return false;
3202 3203

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3204
        return false;
3205 3206 3207

    hassave = virDomainHasManagedSaveImage(dom, 0);
    if (hassave < 0) {
3208
        vshError(ctl, "%s", _("Failed to check for domain managed save image"));
3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224
        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 已提交
3225
    ret = true;
3226 3227 3228 3229 3230 3231

cleanup:
    virDomainFree(dom);
    return ret;
}

3232 3233 3234
/*
 * "schedinfo" command
 */
3235
static const vshCmdInfo info_schedinfo[] = {
3236 3237
    {"help", N_("show/set scheduler parameters")},
    {"desc", N_("Show/Set scheduler parameters.")},
3238 3239 3240
    {NULL, NULL}
};

3241
static const vshCmdOptDef opts_schedinfo[] = {
3242 3243 3244 3245
    {"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")},
3246 3247 3248
    {"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")},
3249 3250 3251
    {NULL, 0, 0, NULL}
};

W
Wen Congyang 已提交
3252
static int
3253
cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
3254
                   virTypedParameterPtr param)
3255
{
3256
    const char *data = NULL;
3257 3258 3259

    /* Legacy 'weight' parameter */
    if (STREQ(param->field, "weight") &&
3260
        param->type == VIR_TYPED_PARAM_UINT &&
3261 3262
        vshCommandOptBool(cmd, "weight")) {
        int val;
3263
        if (vshCommandOptInt(cmd, "weight", &val) <= 0) {
3264
            vshError(ctl, "%s", _("Invalid value of weight"));
3265
            return -1;
3266
        } else {
3267
            param->value.ui = val;
3268
        }
3269
        return 1;
3270 3271
    }

3272 3273
    /* Legacy 'cap' parameter */
    if (STREQ(param->field, "cap") &&
3274
        param->type == VIR_TYPED_PARAM_UINT &&
3275 3276
        vshCommandOptBool(cmd, "cap")) {
        int val;
3277
        if (vshCommandOptInt(cmd, "cap", &val) <= 0) {
3278
            vshError(ctl, "%s", _("Invalid value of cap"));
3279
            return -1;
3280
        } else {
3281
            param->value.ui = val;
3282
        }
3283
        return 1;
3284
    }
3285

3286
    if (vshCommandOptString(cmd, "set", &data) > 0) {
3287 3288 3289
        char *val = strchr(data, '=');
        int match = 0;
        if (!val) {
3290
            vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
3291
            return -1;
3292
        }
3293 3294 3295 3296 3297 3298 3299 3300 3301
        *val = '\0';
        match = STREQ(data, param->field);
        *val = '=';
        val++;

        if (!match)
            return 0;

        switch (param->type) {
3302
        case VIR_TYPED_PARAM_INT:
3303
            if (virStrToLong_i(val, NULL, 10, &param->value.i) < 0) {
3304
                vshError(ctl, "%s",
3305 3306 3307 3308
                         _("Invalid value for parameter, expecting an int"));
                return -1;
            }
            break;
3309
        case VIR_TYPED_PARAM_UINT:
3310
            if (virStrToLong_ui(val, NULL, 10, &param->value.ui) < 0) {
3311
                vshError(ctl, "%s",
3312 3313 3314 3315
                         _("Invalid value for parameter, expecting an unsigned int"));
                return -1;
            }
            break;
3316
        case VIR_TYPED_PARAM_LLONG:
3317
            if (virStrToLong_ll(val, NULL, 10, &param->value.l) < 0) {
3318
                vshError(ctl, "%s",
J
Jim Meyering 已提交
3319
                         _("Invalid value for parameter, expecting a long long"));
3320 3321 3322
                return -1;
            }
            break;
3323
        case VIR_TYPED_PARAM_ULLONG:
3324
            if (virStrToLong_ull(val, NULL, 10, &param->value.ul) < 0) {
3325
                vshError(ctl, "%s",
3326 3327 3328 3329
                         _("Invalid value for parameter, expecting an unsigned long long"));
                return -1;
            }
            break;
3330
        case VIR_TYPED_PARAM_DOUBLE:
3331
            if (virStrToDouble(val, NULL, &param->value.d) < 0) {
3332
                vshError(ctl, "%s", _("Invalid value for parameter, expecting a double"));
3333 3334 3335
                return -1;
            }
            break;
3336
        case VIR_TYPED_PARAM_BOOLEAN:
3337
            param->value.b = STREQ(val, "0") ? 0 : 1;
3338
        }
3339
        return 1;
3340
    }
3341

3342 3343
    return 0;
}
3344

3345

E
Eric Blake 已提交
3346
static bool
3347 3348 3349 3350
cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
{
    char *schedulertype;
    virDomainPtr dom;
3351
    virTypedParameterPtr params = NULL;
3352 3353 3354
    int nparams = 0;
    int update = 0;
    int i, ret;
W
Wen Congyang 已提交
3355
    bool ret_val = false;
3356 3357 3358 3359 3360 3361 3362 3363 3364 3365
    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;
        }
3366
        flags = VIR_DOMAIN_AFFECT_CURRENT;
3367 3368
    } else {
        if (config)
3369
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
3370
        if (live)
3371
            flags |= VIR_DOMAIN_AFFECT_LIVE;
3372
    }
3373

3374
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3375
        return false;
3376

3377
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
3378
        return false;
3379 3380 3381

    /* Print SchedulerType */
    schedulertype = virDomainGetSchedulerType(dom, &nparams);
3382
    if (schedulertype != NULL) {
3383
        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"),
3384
             schedulertype);
3385
        VIR_FREE(schedulertype);
3386
    } else {
3387
        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
3388
        goto cleanup;
3389 3390
    }

3391
    if (nparams) {
3392
        params = vshMalloc(ctl, sizeof(*params) * nparams);
3393

3394
        memset(params, 0, sizeof(*params) * nparams);
3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405
        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);
        }
3406 3407 3408 3409
        if (ret == -1)
            goto cleanup;

        /* See if any params are being set */
3410
        for (i = 0; i < nparams; i++) {
3411 3412 3413 3414 3415 3416 3417 3418 3419 3420
            ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
            if (ret == -1)
                goto cleanup;

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

        /* Update parameters & refresh data */
        if (update) {
3421 3422 3423 3424 3425
            if (flags || current)
                ret = virDomainSetSchedulerParametersFlags(dom, params,
                                                           nparams, flags);
            else
                ret = virDomainSetSchedulerParameters(dom, params, nparams);
3426 3427 3428
            if (ret == -1)
                goto cleanup;

3429 3430 3431 3432 3433 3434 3435
            if (flags || current)
                ret = virDomainGetSchedulerParametersFlags(dom, params,
                                                           &nparams,
                                                           ((live && config) ? 0
                                                            : flags));
            else
                ret = virDomainGetSchedulerParameters(dom, params, &nparams);
3436 3437
            if (ret == -1)
                goto cleanup;
3438 3439 3440 3441
        } 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.  */
3442 3443
            const char *var_value_pair = NULL;
            if (vshCommandOptString(cmd, "set", &var_value_pair) > 0) {
3444 3445 3446 3447
                vshError(ctl, _("invalid scheduler option: %s"),
                         var_value_pair);
                goto cleanup;
            }
3448 3449 3450 3451 3452 3453
            /* 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;
            }
3454 3455
        }

E
Eric Blake 已提交
3456
        ret_val = true;
3457
        for (i = 0; i < nparams; i++) {
3458 3459 3460
            char *str = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
            VIR_FREE(str);
3461 3462
        }
    }
3463

3464
 cleanup:
3465
    VIR_FREE(params);
3466
    virDomainFree(dom);
3467
    return ret_val;
3468 3469
}

3470 3471 3472
/*
 * "restore" command
 */
3473
static const vshCmdInfo info_restore[] = {
3474 3475
    {"help", N_("restore a domain from a saved state in a file")},
    {"desc", N_("Restore a domain.")},
3476
    {NULL, NULL}
3477 3478
};

3479
static const vshCmdOptDef opts_restore[] = {
3480
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the state to restore")},
3481 3482
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when restoring")},
3483 3484
    {"xml", VSH_OT_STRING, 0,
     N_("filename containing updated XML for the target")},
3485 3486
    {"running", VSH_OT_BOOL, 0, N_("restore domain into running state")},
    {"paused", VSH_OT_BOOL, 0, N_("restore domain into paused state")},
3487
    {NULL, 0, 0, NULL}
3488 3489
};

E
Eric Blake 已提交
3490
static bool
3491
cmdRestore(vshControl *ctl, const vshCmd *cmd)
3492
{
3493
    const char *from = NULL;
3494
    bool ret = false;
E
Eric Blake 已提交
3495
    unsigned int flags = 0;
3496 3497
    const char *xmlfile = NULL;
    char *xml = NULL;
3498

3499
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3500
        return false;
3501

3502
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
3503
        return false;
3504

3505 3506
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
3507 3508 3509 3510
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
3511

3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522
    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)
3523
         : virDomainRestore(ctl->conn, from)) < 0) {
3524
        vshError(ctl, _("Failed to restore domain from %s"), from);
3525
        goto cleanup;
3526
    }
3527 3528 3529 3530 3531

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

cleanup:
3532
    VIR_FREE(xml);
3533 3534 3535
    return ret;
}

D
Daniel Veillard 已提交
3536 3537 3538
/*
 * "dump" command
 */
3539
static const vshCmdInfo info_dump[] = {
3540 3541
    {"help", N_("dump the core of a domain to a file for analysis")},
    {"desc", N_("Core dump a domain.")},
D
Daniel Veillard 已提交
3542 3543 3544
    {NULL, NULL}
};

3545
static const vshCmdOptDef opts_dump[] = {
3546 3547
    {"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")},
3548 3549
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when saving")},
3550
    {"reset", VSH_OT_BOOL, 0, N_("reset the domain after core dump")},
3551 3552
    {"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")},
3553
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of dump")},
D
Daniel Veillard 已提交
3554 3555 3556
    {NULL, 0, 0, NULL}
};

3557 3558
static void
doDump(void *opaque)
D
Daniel Veillard 已提交
3559
{
3560 3561 3562 3563 3564 3565
    char ret = '1';
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
    virDomainPtr dom = NULL;
    sigset_t sigmask, oldsigmask;
3566 3567
    const char *name = NULL;
    const char *to = NULL;
E
Eric Blake 已提交
3568
    unsigned int flags = 0;
D
Daniel Veillard 已提交
3569

3570 3571 3572 3573 3574
    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
        goto out_sig;

3575
    if (!vshConnectionUsability(ctl, ctl->conn))
3576
        goto out;
D
Daniel Veillard 已提交
3577

3578
    if (vshCommandOptString(cmd, "file", &to) <= 0)
3579
        goto out;
D
Daniel Veillard 已提交
3580

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

P
Paolo Bonzini 已提交
3584 3585
    if (vshCommandOptBool (cmd, "live"))
        flags |= VIR_DUMP_LIVE;
3586 3587
    if (vshCommandOptBool (cmd, "crash"))
        flags |= VIR_DUMP_CRASH;
3588 3589
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DUMP_BYPASS_CACHE;
3590 3591
    if (vshCommandOptBool(cmd, "reset"))
        flags |= VIR_DUMP_RESET;
3592

3593
    if (virDomainCoreDump(dom, to, flags) < 0) {
3594
        vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
3595
        goto out;
D
Daniel Veillard 已提交
3596 3597
    }

3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646
    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);
3647 3648

cleanup:
D
Daniel Veillard 已提交
3649
    virDomainFree(dom);
3650 3651
    VIR_FORCE_CLOSE(p[0]);
    VIR_FORCE_CLOSE(p[1]);
D
Daniel Veillard 已提交
3652 3653 3654
    return ret;
}

3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728
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;
3729
    bool created = false;
3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768
    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;
        }
3769 3770
    } else {
        created = true;
3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799
    }

    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);
3800
    VIR_FREE(mime);
3801 3802 3803
    return ret;
}

3804 3805 3806
/*
 * "resume" command
 */
3807
static const vshCmdInfo info_resume[] = {
3808 3809
    {"help", N_("resume a domain")},
    {"desc", N_("Resume a previously suspended domain.")},
3810
    {NULL, NULL}
3811 3812
};

3813
static const vshCmdOptDef opts_resume[] = {
3814
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3815
    {NULL, 0, 0, NULL}
3816 3817
};

E
Eric Blake 已提交
3818
static bool
3819
cmdResume(vshControl *ctl, const vshCmd *cmd)
3820
{
3821
    virDomainPtr dom;
E
Eric Blake 已提交
3822
    bool ret = true;
3823
    const char *name;
3824

3825
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3826
        return false;
3827

J
Jim Meyering 已提交
3828
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3829
        return false;
3830 3831

    if (virDomainResume(dom) == 0) {
3832
        vshPrint(ctl, _("Domain %s resumed\n"), name);
3833
    } else {
3834
        vshError(ctl, _("Failed to resume domain %s"), name);
E
Eric Blake 已提交
3835
        ret = false;
3836
    }
3837

3838 3839 3840 3841
    virDomainFree(dom);
    return ret;
}

3842 3843 3844
/*
 * "shutdown" command
 */
3845
static const vshCmdInfo info_shutdown[] = {
3846 3847
    {"help", N_("gracefully shutdown a domain")},
    {"desc", N_("Run shutdown in the target domain.")},
3848
    {NULL, NULL}
3849 3850
};

3851
static const vshCmdOptDef opts_shutdown[] = {
3852
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3853
    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
3854
    {NULL, 0, 0, NULL}
3855 3856
};

E
Eric Blake 已提交
3857
static bool
3858
cmdShutdown(vshControl *ctl, const vshCmd *cmd)
3859
{
3860
    virDomainPtr dom;
E
Eric Blake 已提交
3861
    bool ret = true;
3862
    const char *name;
3863 3864 3865
    const char *mode = NULL;
    int flags = 0;
    int rv;
3866

3867
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3868
        return false;
3869

3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885
    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 已提交
3886
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3887
        return false;
3888

3889 3890 3891 3892 3893
    if (flags)
        rv = virDomainShutdownFlags(dom, flags);
    else
        rv = virDomainShutdown(dom);
    if (rv == 0) {
3894
        vshPrint(ctl, _("Domain %s is being shutdown\n"), name);
3895
    } else {
3896
        vshError(ctl, _("Failed to shutdown domain %s"), name);
E
Eric Blake 已提交
3897
        ret = false;
3898
    }
3899

3900 3901 3902 3903
    virDomainFree(dom);
    return ret;
}

3904 3905 3906
/*
 * "reboot" command
 */
3907
static const vshCmdInfo info_reboot[] = {
3908 3909
    {"help", N_("reboot a domain")},
    {"desc", N_("Run a reboot command in the target domain.")},
3910 3911 3912
    {NULL, NULL}
};

3913
static const vshCmdOptDef opts_reboot[] = {
3914
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3915
    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
3916 3917 3918
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3919
static bool
3920
cmdReboot(vshControl *ctl, const vshCmd *cmd)
3921 3922
{
    virDomainPtr dom;
E
Eric Blake 已提交
3923
    bool ret = true;
3924
    const char *name;
3925 3926
    const char *mode = NULL;
    int flags = 0;
3927

3928
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3929
        return false;
3930

3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946
    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 已提交
3947
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3948
        return false;
3949

3950
    if (virDomainReboot(dom, flags) == 0) {
3951
        vshPrint(ctl, _("Domain %s is being rebooted\n"), name);
3952
    } else {
3953
        vshError(ctl, _("Failed to reboot domain %s"), name);
E
Eric Blake 已提交
3954
        ret = false;
3955 3956 3957 3958 3959 3960
    }

    virDomainFree(dom);
    return ret;
}

X
Xu He Jie 已提交
3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998
/*
 * "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;
}

3999 4000 4001
/*
 * "destroy" command
 */
4002
static const vshCmdInfo info_destroy[] = {
4003 4004 4005
    {"help", N_("destroy (stop) a domain")},
    {"desc",
     N_("Forcefully stop a given domain, but leave its resources intact.")},
4006
    {NULL, NULL}
4007 4008
};

4009
static const vshCmdOptDef opts_destroy[] = {
4010
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4011
    {NULL, 0, 0, NULL}
4012 4013
};

E
Eric Blake 已提交
4014
static bool
4015
cmdDestroy(vshControl *ctl, const vshCmd *cmd)
4016
{
4017
    virDomainPtr dom;
E
Eric Blake 已提交
4018
    bool ret = true;
4019
    const char *name;
4020

4021
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4022
        return false;
4023

J
Jim Meyering 已提交
4024
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
4025
        return false;
4026 4027

    if (virDomainDestroy(dom) == 0) {
4028
        vshPrint(ctl, _("Domain %s destroyed\n"), name);
4029
    } else {
4030
        vshError(ctl, _("Failed to destroy domain %s"), name);
E
Eric Blake 已提交
4031
        ret = false;
4032
    }
4033

4034
    virDomainFree(dom);
K
Karel Zak 已提交
4035 4036 4037 4038
    return ret;
}

/*
4039
 * "dominfo" command
K
Karel Zak 已提交
4040
 */
4041
static const vshCmdInfo info_dominfo[] = {
4042 4043
    {"help", N_("domain information")},
    {"desc", N_("Returns basic information about the domain.")},
4044
    {NULL, NULL}
K
Karel Zak 已提交
4045 4046
};

4047
static const vshCmdOptDef opts_dominfo[] = {
4048
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4049
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
4050 4051
};

E
Eric Blake 已提交
4052
static bool
4053
cmdDominfo(vshControl *ctl, const vshCmd *cmd)
4054
{
K
Karel Zak 已提交
4055 4056
    virDomainInfo info;
    virDomainPtr dom;
4057
    virSecurityModel secmodel;
4058
    virSecurityLabelPtr seclabel;
4059
    int persistent = 0;
E
Eric Blake 已提交
4060 4061
    bool ret = true;
    int autostart;
4062
    unsigned int id;
4063
    char *str, uuid[VIR_UUID_STRING_BUFLEN];
4064
    int has_managed_save = 0;
4065

4066
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4067
        return false;
K
Karel Zak 已提交
4068

J
Jim Meyering 已提交
4069
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4070
        return false;
4071

4072 4073
    id = virDomainGetID(dom);
    if (id == ((unsigned int)-1))
4074
        vshPrint(ctl, "%-15s %s\n", _("Id:"), "-");
4075
    else
4076
        vshPrint(ctl, "%-15s %d\n", _("Id:"), id);
4077 4078
    vshPrint(ctl, "%-15s %s\n", _("Name:"), virDomainGetName(dom));

K
Karel Zak 已提交
4079
    if (virDomainGetUUIDString(dom, &uuid[0])==0)
4080
        vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
4081 4082

    if ((str = virDomainGetOSType(dom))) {
4083
        vshPrint(ctl, "%-15s %s\n", _("OS Type:"), str);
4084
        VIR_FREE(str);
4085 4086 4087
    }

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

4091
        vshPrint(ctl, "%-15s %d\n", _("CPU(s):"), info.nrVirtCpu);
4092 4093

        if (info.cpuTime != 0) {
4094
            double cpuUsed = info.cpuTime;
4095

4096
            cpuUsed /= 1000000000.0;
4097

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

4101 4102
        if (info.maxMem != UINT_MAX)
            vshPrint(ctl, "%-15s %lu kB\n", _("Max memory:"),
4103
                 info.maxMem);
4104
        else
4105
            vshPrint(ctl, "%-15s %s\n", _("Max memory:"),
4106 4107
                 _("no limit"));

4108
        vshPrint(ctl, "%-15s %lu kB\n", _("Used memory:"),
4109 4110
                 info.memory);

K
Karel Zak 已提交
4111
    } else {
E
Eric Blake 已提交
4112
        ret = false;
K
Karel Zak 已提交
4113
    }
4114

4115 4116
    /* Check and display whether the domain is persistent or not */
    persistent = virDomainIsPersistent(dom);
4117 4118
    vshDebug(ctl, VSH_ERR_DEBUG, "Domain persistent flag value: %d\n",
             persistent);
4119 4120 4121 4122 4123 4124
    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 */
4125
    if (!virDomainGetAutostart(dom, &autostart)) {
4126
        vshPrint(ctl, "%-15s %s\n", _("Autostart:"),
4127 4128 4129
                 autostart ? _("enable") : _("disable") );
    }

4130 4131 4132 4133 4134 4135 4136
    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"));

4137 4138 4139
    /* Security model and label information */
    memset(&secmodel, 0, sizeof secmodel);
    if (virNodeGetSecurityModel(ctl->conn, &secmodel) == -1) {
4140 4141
        if (last_error->code != VIR_ERR_NO_SUPPORT) {
            virDomainFree(dom);
E
Eric Blake 已提交
4142
            return false;
4143 4144 4145
        } else {
            virFreeError(last_error);
            last_error = NULL;
4146
        }
4147 4148 4149 4150 4151 4152 4153
    } 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 */
4154
            if (VIR_ALLOC(seclabel) < 0) {
4155
                virDomainFree(dom);
E
Eric Blake 已提交
4156
                return false;
4157 4158 4159 4160 4161
            }

            if (virDomainGetSecurityLabel(dom, seclabel) == -1) {
                virDomainFree(dom);
                VIR_FREE(seclabel);
E
Eric Blake 已提交
4162
                return false;
4163
            } else {
4164
                if (seclabel->label[0] != '\0')
4165
                    vshPrint(ctl, "%-15s %s (%s)\n", _("Security label:"),
4166
                             seclabel->label, seclabel->enforcing ? "enforcing" : "permissive");
4167
            }
4168 4169

            VIR_FREE(seclabel);
4170 4171
        }
    }
4172
    virDomainFree(dom);
K
Karel Zak 已提交
4173 4174 4175
    return ret;
}

4176 4177 4178 4179
/*
 * "domjobinfo" command
 */
static const vshCmdInfo info_domjobinfo[] = {
4180 4181
    {"help", N_("domain job information")},
    {"desc", N_("Returns information about jobs running on a domain.")},
4182 4183 4184 4185
    {NULL, NULL}
};

static const vshCmdOptDef opts_domjobinfo[] = {
4186
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4187 4188 4189 4190
    {NULL, 0, 0, NULL}
};


E
Eric Blake 已提交
4191
static bool
4192 4193 4194 4195
cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
{
    virDomainJobInfo info;
    virDomainPtr dom;
E
Eric Blake 已提交
4196
    bool ret = true;
4197

4198
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4199
        return false;
4200 4201

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4202
        return false;
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

    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 已提交
4229
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
4230
            val = prettyCapacity(info.dataRemaining, &unit);
E
Eric Blake 已提交
4231
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
4232
            val = prettyCapacity(info.dataTotal, &unit);
E
Eric Blake 已提交
4233
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
4234 4235 4236
        }
        if (info.memTotal || info.memRemaining || info.memProcessed) {
            val = prettyCapacity(info.memProcessed, &unit);
E
Eric Blake 已提交
4237
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
4238
            val = prettyCapacity(info.memRemaining, &unit);
E
Eric Blake 已提交
4239
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
4240
            val = prettyCapacity(info.memTotal, &unit);
E
Eric Blake 已提交
4241
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
4242 4243 4244
        }
        if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
            val = prettyCapacity(info.fileProcessed, &unit);
E
Eric Blake 已提交
4245
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
4246
            val = prettyCapacity(info.fileRemaining, &unit);
E
Eric Blake 已提交
4247
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
4248
            val = prettyCapacity(info.fileTotal, &unit);
E
Eric Blake 已提交
4249
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
4250 4251
        }
    } else {
E
Eric Blake 已提交
4252
        ret = false;
4253 4254 4255 4256 4257 4258
    }
cleanup:
    virDomainFree(dom);
    return ret;
}

4259 4260 4261 4262
/*
 * "domjobabort" command
 */
static const vshCmdInfo info_domjobabort[] = {
4263 4264
    {"help", N_("abort active domain job")},
    {"desc", N_("Aborts the currently running domain job")},
4265 4266 4267 4268
    {NULL, NULL}
};

static const vshCmdOptDef opts_domjobabort[] = {
4269
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4270 4271 4272
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4273
static bool
4274 4275 4276
cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
4277
    bool ret = true;
4278

4279
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4280
        return false;
4281 4282

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

    if (virDomainAbortJob(dom) < 0)
E
Eric Blake 已提交
4286
        ret = false;
4287 4288 4289 4290 4291

    virDomainFree(dom);
    return ret;
}

4292 4293 4294
/*
 * "freecell" command
 */
4295
static const vshCmdInfo info_freecell[] = {
4296 4297
    {"help", N_("NUMA free memory")},
    {"desc", N_("display available free memory for the NUMA cell.")},
4298 4299 4300
    {NULL, NULL}
};

4301
static const vshCmdOptDef opts_freecell[] = {
4302
    {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
4303
    {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
4304 4305 4306
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4307
static bool
4308
cmdFreecell(vshControl *ctl, const vshCmd *cmd)
4309
{
W
Wen Congyang 已提交
4310
    bool func_ret = false;
4311
    int ret;
4312
    int cell = -1, cell_given;
4313
    unsigned long long memory;
4314 4315 4316 4317
    xmlNodePtr *nodes = NULL;
    unsigned long nodes_cnt;
    unsigned long *nodes_id = NULL;
    unsigned long long *nodes_free = NULL;
4318
    int all_given;
4319 4320 4321 4322
    int i;
    char *cap_xml = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
4323

4324

4325
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4326
        return false;
4327

4328 4329 4330 4331
    if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
        vshError(ctl, "%s", _("cell number has to be a number"));
        goto cleanup;
    }
4332 4333 4334 4335 4336 4337 4338 4339 4340
    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) {
4341 4342 4343
        cap_xml = virConnectGetCapabilities(ctl->conn);
        if (!cap_xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
4344 4345 4346
            goto cleanup;
        }

4347
        xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
4348 4349
        if (!xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
4350 4351
            goto cleanup;
        }
4352 4353 4354 4355
        nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
                                    ctxt, &nodes);

        if (nodes_cnt == -1) {
4356
            vshError(ctl, "%s", _("could not get information about "
4357
                                  "NUMA topology"));
4358 4359 4360
            goto cleanup;
        }

4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381
        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;
            }
        }

4382
        memory = 0;
4383 4384 4385 4386
        for (cell = 0; cell < nodes_cnt; cell++) {
            vshPrint(ctl, "%5lu: %10llu kB\n", nodes_id[cell],
                    (nodes_free[cell]/1024));
            memory += nodes_free[cell];
4387 4388 4389 4390
        }

        vshPrintExtra(ctl, "--------------------\n");
        vshPrintExtra(ctl, "%5s: %10llu kB\n", _("Total"), memory/1024);
4391
    } else {
4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405
        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));
4406 4407
    }

E
Eric Blake 已提交
4408
    func_ret = true;
4409

4410
cleanup:
4411
    xmlXPathFreeContext(ctxt);
4412
    xmlFreeDoc(xml);
4413
    VIR_FREE(nodes);
4414 4415 4416
    VIR_FREE(nodes_free);
    VIR_FREE(nodes_id);
    VIR_FREE(cap_xml);
4417
    return func_ret;
4418 4419
}

E
Eric Blake 已提交
4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433
/*
 * "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 已提交
4434
static bool
E
Eric Blake 已提交
4435 4436
cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
{
4437
    const char *type = NULL;
E
Eric Blake 已提交
4438 4439
    int vcpus;

4440 4441
    if (vshCommandOptString(cmd, "type", &type) < 0) {
        vshError(ctl, "%s", _("Invalid type"));
E
Eric Blake 已提交
4442
        return false;
4443
    }
E
Eric Blake 已提交
4444 4445

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4446
        return false;
E
Eric Blake 已提交
4447 4448 4449

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

E
Eric Blake 已提交
4453
    return true;
E
Eric Blake 已提交
4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467
}

/*
 * "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")},
4468
    {"active", VSH_OT_BOOL, 0, N_("get number of currently active vcpus")},
E
Eric Blake 已提交
4469
    {"live", VSH_OT_BOOL, 0, N_("get value from running domain")},
4470 4471 4472
    {"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 已提交
4473 4474 4475
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4476
static bool
E
Eric Blake 已提交
4477 4478 4479
cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
4480
    bool ret = true;
E
Eric Blake 已提交
4481
    int maximum = vshCommandOptBool(cmd, "maximum");
4482
    int active = vshCommandOptBool(cmd, "active");
E
Eric Blake 已提交
4483 4484
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
4485 4486
    int current = vshCommandOptBool(cmd, "current");
    bool all = maximum + active + current + config + live == 0;
E
Eric Blake 已提交
4487 4488
    int count;

4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503
    /* 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 已提交
4504
        return false;
E
Eric Blake 已提交
4505
    }
4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518

    /* 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 已提交
4519
        vshError(ctl, "%s",
4520
                 _("--maximum and --active cannot both be specified"));
E
Eric Blake 已提交
4521
        return false;
E
Eric Blake 已提交
4522
    }
4523 4524 4525
    if (current + config + live > 1) {
        vshError(ctl, "%s",
                 _("--config, --live, and --current are mutually exclusive"));
E
Eric Blake 已提交
4526
        return false;
E
Eric Blake 已提交
4527 4528 4529
    }

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4530
        return false;
E
Eric Blake 已提交
4531 4532

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

    /* In all cases, try the new API first; if it fails because we are
4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549
     * 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 已提交
4550 4551
    if (all || (maximum && config)) {
        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
4552
                                             VIR_DOMAIN_AFFECT_CONFIG));
E
Eric Blake 已提交
4553 4554 4555 4556 4557 4558 4559 4560 4561
        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;
            }
4562 4563
            virFreeError(last_error);
            last_error = NULL;
E
Eric Blake 已提交
4564 4565 4566 4567 4568
            VIR_FREE(xml);
        }

        if (count < 0) {
            virshReportError(ctl);
E
Eric Blake 已提交
4569
            ret = false;
E
Eric Blake 已提交
4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581
        } 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 |
4582
                                             VIR_DOMAIN_AFFECT_LIVE));
E
Eric Blake 已提交
4583 4584 4585 4586 4587 4588 4589
        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 已提交
4590
            ret = false;
E
Eric Blake 已提交
4591 4592 4593 4594 4595 4596 4597 4598 4599 4600
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

4601
    if (all || (active && config)) {
4602
        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_CONFIG);
E
Eric Blake 已提交
4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626
        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 已提交
4627
            ret = false;
E
Eric Blake 已提交
4628 4629 4630 4631 4632 4633 4634 4635 4636 4637
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

4638
    if (all || (active && live)) {
4639
        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_LIVE);
E
Eric Blake 已提交
4640 4641 4642 4643 4644 4645 4646 4647 4648
        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 已提交
4649
            ret = false;
E
Eric Blake 已提交
4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663
        } 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;
}

4664 4665 4666
/*
 * "vcpuinfo" command
 */
4667
static const vshCmdInfo info_vcpuinfo[] = {
E
Eric Blake 已提交
4668
    {"help", N_("detailed domain vcpu information")},
4669
    {"desc", N_("Returns basic information about the domain virtual CPUs.")},
4670 4671 4672
    {NULL, NULL}
};

4673
static const vshCmdOptDef opts_vcpuinfo[] = {
4674
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4675 4676 4677
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4678
static bool
4679
cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
4680 4681 4682 4683 4684
{
    virDomainInfo info;
    virDomainPtr dom;
    virNodeInfo nodeinfo;
    virVcpuInfoPtr cpuinfo;
4685 4686
    unsigned char *cpumaps;
    int ncpus, maxcpu;
4687
    size_t cpumaplen;
E
Eric Blake 已提交
4688
    bool ret = true;
4689
    int n, m;
4690

4691
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4692
        return false;
4693

J
Jim Meyering 已提交
4694
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4695
        return false;
4696 4697 4698

    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
4699
        return false;
4700 4701 4702 4703
    }

    if (virDomainGetInfo(dom, &info) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
4704
        return false;
4705 4706
    }

4707
    cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
4708 4709 4710
    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
    cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
4711

4712 4713
    if ((ncpus = virDomainGetVcpus(dom,
                                   cpuinfo, info.nrVirtCpu,
4714
                                   cpumaps, cpumaplen)) >= 0) {
4715 4716 4717 4718
        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 已提交
4719
                     _(vshDomainVcpuStateToString(cpuinfo[n].state)));
4720 4721 4722 4723 4724 4725 4726 4727
            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:"));
4728 4729
            for (m = 0; m < maxcpu; m++) {
                vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
4730 4731 4732 4733 4734 4735
            }
            vshPrint(ctl, "\n");
            if (n < (ncpus - 1)) {
                vshPrint(ctl, "\n");
            }
        }
4736
    } else {
4737
        if (info.state == VIR_DOMAIN_SHUTOFF &&
E
Eric Blake 已提交
4738
            (ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
4739 4740 4741
                                             cpumaps, cpumaplen,
                                             VIR_DOMAIN_AFFECT_CONFIG)) >= 0) {

E
Eric Blake 已提交
4742
            /* fallback plan to use virDomainGetVcpuPinInfo */
4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760

            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;
4761
        }
4762 4763
    }

4764
    VIR_FREE(cpumaps);
4765
    VIR_FREE(cpuinfo);
4766 4767 4768 4769 4770 4771 4772
    virDomainFree(dom);
    return ret;
}

/*
 * "vcpupin" command
 */
4773
static const vshCmdInfo info_vcpupin[] = {
4774
    {"help", N_("control or query domain vcpu affinity")},
4775
    {"desc", N_("Pin domain VCPUs to host physical CPUs.")},
4776 4777 4778
    {NULL, NULL}
};

4779
static const vshCmdOptDef opts_vcpupin[] = {
4780
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4781 4782 4783
    {"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")},
4784 4785 4786
    {"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")},
4787 4788 4789
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4790
static bool
E
Eric Blake 已提交
4791
cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
4792 4793 4794 4795
{
    virDomainInfo info;
    virDomainPtr dom;
    virNodeInfo nodeinfo;
4796
    int vcpu = -1;
4797
    const char *cpulist = NULL;
E
Eric Blake 已提交
4798
    bool ret = true;
4799 4800
    unsigned char *cpumap = NULL;
    unsigned char *cpumaps = NULL;
E
Eric Blake 已提交
4801
    size_t cpumaplen;
4802 4803
    bool bit, lastbit, isInvert;
    int i, cpu, lastcpu, maxcpu, ncpus;
4804
    bool unuse = false;
4805
    const char *cur;
4806 4807 4808
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    int current = vshCommandOptBool(cmd, "current");
4809
    bool query = false; /* Query mode if no cpulist */
E
Eric Blake 已提交
4810
    unsigned int flags = 0;
4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826

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

4828
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4829
        return false;
4830

J
Jim Meyering 已提交
4831
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4832
        return false;
4833

4834 4835
    if (vshCommandOptString(cmd, "cpulist", &cpulist) < 0) {
        vshError(ctl, "%s", _("vcpupin: Missing cpulist."));
4836
        virDomainFree(dom);
E
Eric Blake 已提交
4837
        return false;
4838
    }
4839
    query = !cpulist;
4840

4841 4842 4843 4844
    /* In query mode, "vcpu" is optional */
    if (vshCommandOptInt(cmd, "vcpu", &vcpu) < !query) {
        vshError(ctl, "%s",
                 _("vcpupin: Invalid or missing vCPU number."));
4845
        virDomainFree(dom);
E
Eric Blake 已提交
4846
        return false;
4847
    }
4848

4849 4850
    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
4851
        return false;
4852 4853 4854
    }

    if (virDomainGetInfo(dom, &info) != 0) {
4855
        vshError(ctl, "%s", _("vcpupin: failed to get domain information."));
4856
        virDomainFree(dom);
E
Eric Blake 已提交
4857
        return false;
4858 4859 4860
    }

    if (vcpu >= info.nrVirtCpu) {
4861
        vshError(ctl, "%s", _("vcpupin: Invalid vCPU number."));
4862
        virDomainFree(dom);
E
Eric Blake 已提交
4863
        return false;
4864 4865
    }

4866 4867
    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
4868

4869 4870 4871 4872 4873 4874 4875 4876
    /* 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 已提交
4877
        if ((ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922
                                             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*/

    cpumap = vshCalloc(ctl, 0, cpumaplen);
4923 4924
    /* Parse cpulist */
    cur = cpulist;
4925
    if (*cur == 0) {
4926
        goto parse_error;
4927 4928 4929
    } else if (*cur == 'r') {
        for (cpu = 0; cpu < maxcpu; cpu++)
            VIR_USE_CPU(cpumap, cpu);
4930
        cur = "";
4931
    }
4932 4933 4934 4935 4936 4937 4938

    while (*cur != 0) {

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

4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952
        /* 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);
4953

4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980
        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);
        }
4981

4982 4983 4984 4985 4986 4987
        if (*cur == ',') {
            cur++;
            virSkipSpaces(&cur);
            unuse = false;
        } else if (*cur == 0) {
            break;
4988
        } else {
4989
            goto parse_error;
4990
        }
4991
    }
4992

4993 4994 4995 4996 4997 4998 4999 5000
    if (flags == -1) {
        if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) {
            ret = false;
        }
    } else {
        if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0) {
            ret = false;
        }
5001 5002
    }

5003
cleanup:
5004
    VIR_FREE(cpumap);
5005 5006
    virDomainFree(dom);
    return ret;
5007 5008 5009 5010 5011

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

5014 5015 5016
/*
 * "setvcpus" command
 */
5017
static const vshCmdInfo info_setvcpus[] = {
5018 5019
    {"help", N_("change number of virtual CPUs")},
    {"desc", N_("Change the number of virtual CPUs in the guest domain.")},
5020 5021 5022
    {NULL, NULL}
};

5023
static const vshCmdOptDef opts_setvcpus[] = {
5024
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5025
    {"count", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of virtual CPUs")},
E
Eric Blake 已提交
5026 5027 5028
    {"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")},
5029
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
5030 5031 5032
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5033
static bool
5034
cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
5035 5036
{
    virDomainPtr dom;
5037
    int count = 0;
E
Eric Blake 已提交
5038
    bool ret = true;
E
Eric Blake 已提交
5039 5040 5041
    int maximum = vshCommandOptBool(cmd, "maximum");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
5042
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
5043
    unsigned int flags = 0;
5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059

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

5061
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5062
        return false;
5063

J
Jim Meyering 已提交
5064
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5065
        return false;
5066

5067 5068 5069 5070
    if (vshCommandOptInt(cmd, "count", &count) < 0) {
        vshError(ctl, "%s", _("Invalid number of virtual CPUs"));
        goto cleanup;
    }
5071

5072
    if (flags == -1) {
E
Eric Blake 已提交
5073
        if (virDomainSetVcpus(dom, count) != 0) {
E
Eric Blake 已提交
5074
            ret = false;
E
Eric Blake 已提交
5075 5076
        }
    } else {
5077 5078 5079
        /* If the --maximum flag was given, we need to ensure only the
           --config flag is in effect as well */
        if (maximum) {
5080
            vshDebug(ctl, VSH_ERR_DEBUG, "--maximum flag was given\n");
5081

5082 5083
            flags |= VIR_DOMAIN_VCPU_MAXIMUM;

5084 5085 5086 5087 5088 5089 5090 5091
            /* 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 已提交
5092
                ret = false;
5093 5094 5095 5096 5097
                goto cleanup;
            }
        }

        /* Apply the virtual cpu changes */
E
Eric Blake 已提交
5098
        if (virDomainSetVcpusFlags(dom, count, flags) < 0) {
E
Eric Blake 已提交
5099
            ret = false;
E
Eric Blake 已提交
5100
        }
5101 5102
    }

5103
  cleanup:
5104 5105 5106 5107
    virDomainFree(dom);
    return ret;
}

5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141
/*
 * "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;
}

5142 5143 5144 5145 5146
/*
 * "send-key" command
 */
static const vshCmdInfo info_send_key[] = {
    {"help", N_("Send keycodes to the guest")},
5147
    {"desc", N_("Send keycodes (integers or symbolic names) to the guest")},
5148 5149 5150 5151 5152
    {NULL, NULL}
};

static const vshCmdOptDef opts_send_key[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5153 5154
    {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT,
     N_("the codeset of keycodes, default:linux")},
5155
    {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT,
A
Alex Jia 已提交
5156
     N_("the time (in milliseconds) how long the keys will be held")},
5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228
    {"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;
}

5229 5230 5231
/*
 * "setmemory" command
 */
5232
static const vshCmdInfo info_setmem[] = {
5233 5234
    {"help", N_("change memory allocation")},
    {"desc", N_("Change the current memory allocation in the guest domain.")},
5235 5236 5237
    {NULL, NULL}
};

5238
static const vshCmdOptDef opts_setmem[] = {
5239
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5240
    {"kilobytes", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of kilobytes of memory")},
5241 5242
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
5243
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
5244 5245 5246
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5247
static bool
5248
cmdSetmem(vshControl *ctl, const vshCmd *cmd)
5249 5250
{
    virDomainPtr dom;
5251
    virDomainInfo info;
5252
    unsigned long kilobytes = 0;
E
Eric Blake 已提交
5253
    bool ret = true;
5254 5255
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
5256
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
5257
    unsigned int flags = 0;
5258

5259 5260 5261
    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
E
Eric Blake 已提交
5262
            return false;
5263
        }
5264
        flags = VIR_DOMAIN_AFFECT_CURRENT;
5265 5266
    } else {
        if (config)
5267
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
5268
        if (live)
5269
            flags |= VIR_DOMAIN_AFFECT_LIVE;
5270 5271 5272
        /* neither option is specified */
        if (!live && !config)
            flags = -1;
5273
    }
5274

5275
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5276
        return false;
5277

J
Jim Meyering 已提交
5278
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5279
        return false;
5280

5281 5282
    if (vshCommandOptUL(cmd, "kilobytes", &kilobytes) < 0) {
        vshError(ctl, "%s", _("memory size has to be a number"));
E
Eric Blake 已提交
5283
        return false;
5284 5285
    }

5286
    if (kilobytes <= 0) {
5287
        virDomainFree(dom);
5288
        vshError(ctl, _("Invalid value of %lu for memory size"), kilobytes);
E
Eric Blake 已提交
5289
        return false;
5290 5291
    }

5292 5293
    if (virDomainGetInfo(dom, &info) != 0) {
        virDomainFree(dom);
5294
        vshError(ctl, "%s", _("Unable to verify MaxMemorySize"));
E
Eric Blake 已提交
5295
        return false;
5296 5297 5298 5299
    }

    if (kilobytes > info.maxMem) {
        virDomainFree(dom);
5300
        vshError(ctl, _("Requested memory size %lu kb is larger than maximum of %lu kb"),
5301
                 kilobytes, info.maxMem);
E
Eric Blake 已提交
5302
        return false;
5303 5304
    }

5305
    if (flags == -1) {
5306
        if (virDomainSetMemory(dom, kilobytes) != 0) {
E
Eric Blake 已提交
5307
            ret = false;
5308 5309 5310
        }
    } else {
        if (virDomainSetMemoryFlags(dom, kilobytes, flags) < 0) {
E
Eric Blake 已提交
5311
            ret = false;
5312
        }
5313 5314 5315 5316 5317 5318 5319 5320 5321
    }

    virDomainFree(dom);
    return ret;
}

/*
 * "setmaxmem" command
 */
5322
static const vshCmdInfo info_setmaxmem[] = {
5323 5324
    {"help", N_("change maximum memory limit")},
    {"desc", N_("Change the maximum memory allocation limit in the guest domain.")},
5325 5326 5327
    {NULL, NULL}
};

5328
static const vshCmdOptDef opts_setmaxmem[] = {
5329
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5330
    {"kilobytes", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum memory limit in kilobytes")},
5331 5332 5333
    {"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")},
5334 5335 5336
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5337
static bool
5338
cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
5339 5340
{
    virDomainPtr dom;
5341
    int kilobytes = 0;
E
Eric Blake 已提交
5342
    bool ret = true;
5343 5344 5345
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
5346
    unsigned int flags = VIR_DOMAIN_MEM_MAXIMUM;
5347 5348 5349 5350

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
E
Eric Blake 已提交
5351
            return false;
5352 5353 5354
        }
    } else {
        if (config)
5355
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
5356
        if (live)
5357
            flags |= VIR_DOMAIN_AFFECT_LIVE;
5358 5359 5360 5361
        /* neither option is specified */
        if (!live && !config)
            flags = -1;
    }
5362

5363
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5364
        return false;
5365

J
Jim Meyering 已提交
5366
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5367
        return false;
5368

5369 5370
    if (vshCommandOptInt(cmd, "kilobytes", &kilobytes) < 0) {
        vshError(ctl, "%s", _("memory size has to be a number"));
E
Eric Blake 已提交
5371
        return false;
5372 5373
    }

5374
    if (kilobytes <= 0) {
5375
        virDomainFree(dom);
5376
        vshError(ctl, _("Invalid value of %d for memory size"), kilobytes);
E
Eric Blake 已提交
5377
        return false;
5378 5379
    }

5380 5381 5382
    if (flags == -1) {
        if (virDomainSetMaxMemory(dom, kilobytes) != 0) {
            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
E
Eric Blake 已提交
5383
            ret = false;
5384 5385 5386 5387
        }
    } else {
        if (virDomainSetMemoryFlags(dom, kilobytes, flags) < 0) {
            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
E
Eric Blake 已提交
5388
            ret = false;
5389
        }
5390 5391
    }

5392 5393 5394 5395
    virDomainFree(dom);
    return ret;
}

5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411
/*
 * "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]")},
5412 5413
    {"device-weights", VSH_OT_STRING, VSH_OFLAG_NONE,
     N_("per-device IO Weights, in the form of /path/to/device,weight,...")},
H
Hu Tao 已提交
5414 5415 5416
    {"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")},
5417 5418 5419
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5420
static bool
5421 5422 5423
cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
{
    virDomainPtr dom;
5424
    const char *device_weight = NULL;
5425 5426
    int weight = 0;
    int nparams = 0;
5427
    int rv = 0;
5428
    unsigned int i = 0;
5429
    virTypedParameterPtr params = NULL, temp = NULL;
E
Eric Blake 已提交
5430
    bool ret = false;
H
Hu Tao 已提交
5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447
    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;
    }
5448 5449

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5450
        return false;
5451 5452

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

5455
    if ((rv = vshCommandOptInt(cmd, "weight", &weight)) < 0) {
5456 5457 5458 5459 5460
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    }

5461
    if (rv > 0) {
5462
        nparams++;
5463
        if (weight <= 0) {
5464 5465 5466 5467 5468
            vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
            goto cleanup;
        }
    }

5469 5470 5471 5472 5473 5474 5475 5476 5477 5478
    rv = vshCommandOptString(cmd, "device-weights", &device_weight);
    if (rv < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse string parameter"));
        goto cleanup;
    }
    if (rv > 0) {
        nparams++;
    }

5479 5480
    if (nparams == 0) {
        /* get the number of blkio parameters */
H
Hu Tao 已提交
5481
        if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
5482 5483 5484 5485 5486 5487 5488
            vshError(ctl, "%s",
                     _("Unable to get number of blkio parameters"));
            goto cleanup;
        }

        if (nparams == 0) {
            /* nothing to output */
E
Eric Blake 已提交
5489
            ret = true;
5490 5491 5492 5493 5494
            goto cleanup;
        }

        /* now go get all the blkio parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));
H
Hu Tao 已提交
5495
        if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
5496 5497 5498 5499 5500
            vshError(ctl, "%s", _("Unable to get blkio parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
5501 5502 5503
            char *str = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
            VIR_FREE(str);
5504 5505 5506 5507 5508 5509 5510
        }
    } else {
        /* set the blkio parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));

        for (i = 0; i < nparams; i++) {
            temp = &params[i];
5511
            temp->type = VIR_TYPED_PARAM_UINT;
5512 5513 5514

            if (weight) {
                temp->value.ui = weight;
5515 5516 5517
                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
                               sizeof(temp->field)))
                    goto cleanup;
5518 5519
                weight = 0;
            } else if (device_weight) {
5520 5521 5522 5523 5524
                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;
5525
                device_weight = NULL;
5526 5527
            }
        }
5528 5529

        if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0) {
5530
            vshError(ctl, "%s", _("Unable to change blkio parameters"));
5531 5532
            goto cleanup;
        }
5533 5534
    }

5535 5536
    ret = true;

5537
  cleanup:
5538
    virTypedParameterArrayClear(params, nparams);
5539 5540 5541 5542 5543
    VIR_FREE(params);
    virDomainFree(dom);
    return ret;
}

5544 5545 5546 5547
/*
 * "memtune" command
 */
static const vshCmdInfo info_memtune[] = {
5548 5549 5550
    {"help", N_("Get or set memory parameters")},
    {"desc", N_("Get or set the current memory parameters for a guest" \
                " domain.\n" \
5551 5552 5553 5554 5555 5556 5557
                "    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")},
5558
    {"hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
5559
     N_("Max memory in kilobytes")},
5560
    {"soft-limit", VSH_OT_INT, VSH_OFLAG_NONE,
5561
     N_("Memory during contention in kilobytes")},
5562
    {"swap-hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
5563
     N_("Max memory plus swap in kilobytes")},
5564
    {"min-guarantee", VSH_OT_INT, VSH_OFLAG_NONE,
5565
     N_("Min guaranteed memory in kilobytes")},
5566 5567 5568
    {"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")},
5569 5570 5571
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5572
static bool
5573 5574 5575
cmdMemtune(vshControl * ctl, const vshCmd * cmd)
{
    virDomainPtr dom;
5576 5577
    long long hard_limit = 0, soft_limit = 0, swap_hard_limit = 0;
    long long min_guarantee = 0;
5578 5579
    int nparams = 0;
    unsigned int i = 0;
5580
    virTypedParameterPtr params = NULL, temp = NULL;
E
Eric Blake 已提交
5581
    bool ret = false;
5582 5583 5584 5585 5586 5587 5588 5589 5590 5591
    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;
        }
5592
        flags = VIR_DOMAIN_AFFECT_CURRENT;
5593 5594
    } else {
        if (config)
5595
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
5596
        if (live)
5597
            flags |= VIR_DOMAIN_AFFECT_LIVE;
5598
    }
5599 5600

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5601
        return false;
5602 5603

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

5606 5607 5608 5609 5610 5611 5612 5613 5614
    if (vshCommandOptLongLong(cmd, "hard-limit", &hard_limit) < 0 ||
        vshCommandOptLongLong(cmd, "soft-limit", &soft_limit) < 0 ||
        vshCommandOptLongLong(cmd, "swap-hard-limit", &swap_hard_limit) < 0 ||
        vshCommandOptLongLong(cmd, "min-guarantee", &min_guarantee) < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    }

5615 5616 5617 5618 5619 5620 5621 5622 5623
    if (hard_limit)
        nparams++;

    if (soft_limit)
        nparams++;

    if (swap_hard_limit)
        nparams++;

5624 5625 5626
    if (min_guarantee)
        nparams++;

5627 5628
    if (nparams == 0) {
        /* get the number of memory parameters */
5629
        if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
5630 5631 5632 5633 5634
            vshError(ctl, "%s",
                     _("Unable to get number of memory parameters"));
            goto cleanup;
        }

5635 5636
        if (nparams == 0) {
            /* nothing to output */
E
Eric Blake 已提交
5637
            ret = true;
5638 5639 5640
            goto cleanup;
        }

5641
        /* now go get all the memory parameters */
E
Eric Blake 已提交
5642
        params = vshCalloc(ctl, nparams, sizeof(*params));
5643
        if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
5644 5645 5646 5647 5648
            vshError(ctl, "%s", _("Unable to get memory parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
5649 5650 5651 5652 5653 5654 5655
            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);
5656 5657 5658
            }
        }

E
Eric Blake 已提交
5659
        ret = true;
5660 5661
    } else {
        /* set the memory parameters */
E
Eric Blake 已提交
5662
        params = vshCalloc(ctl, nparams, sizeof(*params));
5663 5664 5665

        for (i = 0; i < nparams; i++) {
            temp = &params[i];
5666
            temp->type = VIR_TYPED_PARAM_ULLONG;
5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685

            /*
             * 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;
5686
                strncpy(temp->field, VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
5687 5688
                        sizeof(temp->field));
                swap_hard_limit = 0;
5689 5690 5691 5692 5693
            } else if (min_guarantee) {
                temp->value.ul = min_guarantee;
                strncpy(temp->field, VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
                        sizeof(temp->field));
                min_guarantee = 0;
5694
            }
5695 5696 5697 5698

            /* If the user has passed -1, we interpret it as unlimited */
            if (temp->value.ul == -1)
                temp->value.ul = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
5699
        }
5700
        if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
5701
            vshError(ctl, "%s", _("Unable to change memory parameters"));
5702
        else
E
Eric Blake 已提交
5703
            ret = true;
5704 5705 5706
    }

  cleanup:
E
Eric Blake 已提交
5707
    VIR_FREE(params);
5708 5709 5710 5711
    virDomainFree(dom);
    return ret;
}

H
Hu Tao 已提交
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 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 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 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869
/*
 * "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;
}

5870 5871 5872
/*
 * "nodeinfo" command
 */
5873
static const vshCmdInfo info_nodeinfo[] = {
5874 5875
    {"help", N_("node information")},
    {"desc", N_("Returns basic information about the node.")},
5876 5877 5878
    {NULL, NULL}
};

E
Eric Blake 已提交
5879
static bool
5880
cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
5881 5882
{
    virNodeInfo info;
5883

5884
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5885
        return false;
5886 5887

    if (virNodeGetInfo(ctl->conn, &info) < 0) {
5888
        vshError(ctl, "%s", _("failed to get node information"));
E
Eric Blake 已提交
5889
        return false;
5890 5891 5892 5893 5894 5895 5896 5897 5898 5899
    }
    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 已提交
5900
    return true;
5901 5902
}

5903 5904 5905 5906 5907
/*
 * "nodecpustats" command
 */
static const vshCmdInfo info_nodecpustats[] = {
    {"help", N_("Prints cpu stats of the node.")},
5908
    {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
5909 5910 5911 5912 5913 5914 5915 5916 5917 5918
    {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
5919
cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
5920 5921 5922 5923
{
    int i, j;
    bool flag_utilization = false;
    bool flag_percent = vshCommandOptBool(cmd, "percent");
5924 5925
    int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
    virNodeCPUStatsPtr params;
5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968
    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;

5969
            if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
5970
                cpu_stats[i].sys = value;
5971
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
5972
                cpu_stats[i].user = value;
5973
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
5974
                cpu_stats[i].idle = value;
5975
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
5976
                cpu_stats[i].iowait = value;
5977
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991
                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) {
5992
            vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
5993
            vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
5994
            vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
5995 5996 5997 5998 5999 6000 6001
            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);
6002
            vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014
        } 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",
6015
                     _("user:"), user_time / total_time * 100);
6016
            vshPrint(ctl, "%-15s %5.1lf%%\n",
6017
                     _("system:"), sys_time  / total_time * 100);
6018
            vshPrint(ctl, "%-15s %5.1lf%%\n",
6019
                     _("idle:"), idle_time     / total_time * 100);
6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("iowait:"), iowait_time   / total_time * 100);
        }
    }

    ret = true;

  cleanup:
    VIR_FREE(params);
    return ret;
}

6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050
/*
 * "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;
6051 6052
    int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
    virNodeMemoryStatsPtr params = NULL;
6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092
    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;
}

6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121
/*
 * "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;

6122 6123
    if (vshCommandOptString(cmd, "target", &target) < 0) {
        vshError(ctl, _("Invalid target argument"));
6124
        return false;
6125
    }
6126

6127 6128
    if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
        vshError(ctl, _("Invalid duration argument"));
6129
        return false;
6130
    }
6131

6132 6133
    if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
        vshError(ctl, _("Invalid flags argument"));
6134
        return false;
6135
    }
6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161

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


6162 6163 6164
/*
 * "capabilities" command
 */
6165
static const vshCmdInfo info_capabilities[] = {
6166 6167
    {"help", N_("capabilities")},
    {"desc", N_("Returns capabilities of hypervisor/driver.")},
6168 6169 6170
    {NULL, NULL}
};

E
Eric Blake 已提交
6171
static bool
6172
cmdCapabilities (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
6173 6174 6175
{
    char *caps;

6176
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6177
        return false;
6178 6179

    if ((caps = virConnectGetCapabilities (ctl->conn)) == NULL) {
6180
        vshError(ctl, "%s", _("failed to get capabilities"));
E
Eric Blake 已提交
6181
        return false;
6182 6183
    }
    vshPrint (ctl, "%s\n", caps);
6184
    VIR_FREE(caps);
6185

E
Eric Blake 已提交
6186
    return true;
6187 6188
}

6189 6190 6191
/*
 * "dumpxml" command
 */
6192
static const vshCmdInfo info_dumpxml[] = {
6193 6194
    {"help", N_("domain information in XML")},
    {"desc", N_("Output the domain information as an XML dump to stdout.")},
6195
    {NULL, NULL}
6196 6197
};

6198
static const vshCmdOptDef opts_dumpxml[] = {
6199 6200 6201
    {"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")},
6202
    {"update-cpu", VSH_OT_BOOL, 0, N_("update guest CPU according to host CPU")},
6203
    {NULL, 0, 0, NULL}
6204 6205
};

E
Eric Blake 已提交
6206
static bool
6207
cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
6208
{
6209
    virDomainPtr dom;
E
Eric Blake 已提交
6210
    bool ret = true;
6211
    char *dump;
E
Eric Blake 已提交
6212
    unsigned int flags = 0;
6213 6214
    int inactive = vshCommandOptBool(cmd, "inactive");
    int secure = vshCommandOptBool(cmd, "security-info");
6215
    int update = vshCommandOptBool(cmd, "update-cpu");
6216 6217 6218 6219 6220

    if (inactive)
        flags |= VIR_DOMAIN_XML_INACTIVE;
    if (secure)
        flags |= VIR_DOMAIN_XML_SECURE;
6221 6222
    if (update)
        flags |= VIR_DOMAIN_XML_UPDATE_CPU;
6223

6224
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6225
        return false;
6226

J
Jim Meyering 已提交
6227
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
6228
        return false;
6229

6230
    dump = virDomainGetXMLDesc(dom, flags);
6231
    if (dump != NULL) {
6232
        vshPrint(ctl, "%s", dump);
6233
        VIR_FREE(dump);
6234
    } else {
E
Eric Blake 已提交
6235
        ret = false;
6236
    }
6237

6238 6239 6240 6241
    virDomainFree(dom);
    return ret;
}

6242 6243 6244 6245
/*
 * "domxml-from-native" command
 */
static const vshCmdInfo info_domxmlfromnative[] = {
6246 6247
    {"help", N_("Convert native config to domain XML")},
    {"desc", N_("Convert native guest configuration format to domain XML format.")},
6248 6249 6250 6251
    {NULL, NULL}
};

static const vshCmdOptDef opts_domxmlfromnative[] = {
6252 6253
    {"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")},
6254 6255 6256
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6257
static bool
6258 6259
cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
6260
    bool ret = true;
6261 6262
    const char *format = NULL;
    const char *configFile = NULL;
6263 6264
    char *configData;
    char *xmlData;
E
Eric Blake 已提交
6265
    unsigned int flags = 0;
6266

6267
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6268
        return false;
6269

6270 6271
    if (vshCommandOptString(cmd, "format", &format) < 0 ||
        vshCommandOptString(cmd, "config", &configFile) < 0)
E
Eric Blake 已提交
6272
        return false;
6273

6274
    if (virFileReadAll(configFile, 1024*1024, &configData) < 0)
E
Eric Blake 已提交
6275
        return false;
6276 6277 6278

    xmlData = virConnectDomainXMLFromNative(ctl->conn, format, configData, flags);
    if (xmlData != NULL) {
6279
        vshPrint(ctl, "%s", xmlData);
6280
        VIR_FREE(xmlData);
6281
    } else {
E
Eric Blake 已提交
6282
        ret = false;
6283 6284
    }

6285
    VIR_FREE(configData);
6286 6287 6288 6289 6290 6291 6292
    return ret;
}

/*
 * "domxml-to-native" command
 */
static const vshCmdInfo info_domxmltonative[] = {
6293 6294
    {"help", N_("Convert domain XML to native config")},
    {"desc", N_("Convert domain XML config to a native guest configuration format.")},
6295 6296 6297 6298
    {NULL, NULL}
};

static const vshCmdOptDef opts_domxmltonative[] = {
6299 6300
    {"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")},
6301 6302 6303
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6304
static bool
6305 6306
cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
6307
    bool ret = true;
6308 6309
    const char *format = NULL;
    const char *xmlFile = NULL;
6310 6311
    char *configData;
    char *xmlData;
E
Eric Blake 已提交
6312
    unsigned int flags = 0;
6313

6314
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6315
        return false;
6316

6317 6318
    if (vshCommandOptString(cmd, "format", &format) < 0
        || vshCommandOptString(cmd, "xml", &xmlFile) < 0)
E
Eric Blake 已提交
6319
        return false;
6320

6321
    if (virFileReadAll(xmlFile, 1024*1024, &xmlData) < 0)
E
Eric Blake 已提交
6322
        return false;
6323 6324 6325

    configData = virConnectDomainXMLToNative(ctl->conn, format, xmlData, flags);
    if (configData != NULL) {
6326
        vshPrint(ctl, "%s", configData);
6327
        VIR_FREE(configData);
6328
    } else {
E
Eric Blake 已提交
6329
        ret = false;
6330 6331
    }

6332
    VIR_FREE(xmlData);
6333 6334 6335
    return ret;
}

K
Karel Zak 已提交
6336
/*
K
Karel Zak 已提交
6337
 * "domname" command
K
Karel Zak 已提交
6338
 */
6339
static const vshCmdInfo info_domname[] = {
6340
    {"help", N_("convert a domain id or UUID to domain name")},
6341
    {"desc", ""},
6342
    {NULL, NULL}
K
Karel Zak 已提交
6343 6344
};

6345
static const vshCmdOptDef opts_domname[] = {
6346
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or uuid")},
6347
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
6348 6349
};

E
Eric Blake 已提交
6350
static bool
6351
cmdDomname(vshControl *ctl, const vshCmd *cmd)
6352
{
K
Karel Zak 已提交
6353 6354
    virDomainPtr dom;

6355
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6356
        return false;
J
Jim Meyering 已提交
6357
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
6358
                                      VSH_BYID|VSH_BYUUID)))
E
Eric Blake 已提交
6359
        return false;
6360

K
Karel Zak 已提交
6361 6362
    vshPrint(ctl, "%s\n", virDomainGetName(dom));
    virDomainFree(dom);
E
Eric Blake 已提交
6363
    return true;
K
Karel Zak 已提交
6364 6365 6366
}

/*
K
Karel Zak 已提交
6367
 * "domid" command
K
Karel Zak 已提交
6368
 */
6369
static const vshCmdInfo info_domid[] = {
6370
    {"help", N_("convert a domain name or UUID to domain id")},
6371
    {"desc", ""},
6372
    {NULL, NULL}
K
Karel Zak 已提交
6373 6374
};

6375
static const vshCmdOptDef opts_domid[] = {
6376
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
6377
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
6378 6379
};

E
Eric Blake 已提交
6380
static bool
6381
cmdDomid(vshControl *ctl, const vshCmd *cmd)
6382
{
6383
    virDomainPtr dom;
6384
    unsigned int id;
K
Karel Zak 已提交
6385

6386
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6387
        return false;
J
Jim Meyering 已提交
6388
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
6389
                                      VSH_BYNAME|VSH_BYUUID)))
E
Eric Blake 已提交
6390
        return false;
6391

6392 6393
    id = virDomainGetID(dom);
    if (id == ((unsigned int)-1))
6394
        vshPrint(ctl, "%s\n", "-");
6395
    else
6396
        vshPrint(ctl, "%d\n", id);
K
Karel Zak 已提交
6397
    virDomainFree(dom);
E
Eric Blake 已提交
6398
    return true;
K
Karel Zak 已提交
6399
}
6400

K
Karel Zak 已提交
6401 6402 6403
/*
 * "domuuid" command
 */
6404
static const vshCmdInfo info_domuuid[] = {
6405
    {"help", N_("convert a domain name or id to domain UUID")},
6406
    {"desc", ""},
K
Karel Zak 已提交
6407 6408 6409
    {NULL, NULL}
};

6410
static const vshCmdOptDef opts_domuuid[] = {
6411
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or name")},
K
Karel Zak 已提交
6412 6413 6414
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6415
static bool
6416
cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
K
Karel Zak 已提交
6417 6418
{
    virDomainPtr dom;
6419
    char uuid[VIR_UUID_STRING_BUFLEN];
K
Karel Zak 已提交
6420

6421
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6422
        return false;
J
Jim Meyering 已提交
6423
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
6424
                                      VSH_BYNAME|VSH_BYID)))
E
Eric Blake 已提交
6425
        return false;
6426

K
Karel Zak 已提交
6427 6428 6429
    if (virDomainGetUUIDString(dom, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
6430
        vshError(ctl, "%s", _("failed to get domain UUID"));
6431

6432
    virDomainFree(dom);
E
Eric Blake 已提交
6433
    return true;
K
Karel Zak 已提交
6434 6435
}

6436 6437 6438
/*
 * "migrate" command
 */
6439
static const vshCmdInfo info_migrate[] = {
6440 6441
    {"help", N_("migrate domain to another host")},
    {"desc", N_("Migrate domain to another host.  Add --live for live migration.")},
6442 6443 6444
    {NULL, NULL}
};

6445
static const vshCmdOptDef opts_migrate[] = {
6446 6447 6448 6449 6450 6451 6452
    {"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")},
    {"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")},
6453 6454
    {"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)")},
6455 6456
    {"change-protection", VSH_OT_BOOL, 0,
     N_("prevent any configuration changes to domain until migration ends)")},
W
Wen Congyang 已提交
6457
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of migration")},
6458
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
6459
    {"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)")},
6460 6461
    {"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 已提交
6462
    {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")},
6463
    {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")},
6464 6465 6466
    {NULL, 0, 0, NULL}
};

6467 6468
static void
doMigrate (void *opaque)
6469
{
6470
    char ret = '1';
6471
    virDomainPtr dom = NULL;
6472 6473 6474
    const char *desturi = NULL;
    const char *migrateuri = NULL;
    const char *dname = NULL;
E
Eric Blake 已提交
6475
    unsigned int flags = 0;
6476 6477 6478
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
6479 6480
    const char *xmlfile = NULL;
    char *xml = NULL;
6481
    sigset_t sigmask, oldsigmask;
6482 6483 6484 6485 6486

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

6488
    if (!vshConnectionUsability (ctl, ctl->conn))
6489
        goto out;
6490

J
Jim Meyering 已提交
6491
    if (!(dom = vshCommandOptDomain (ctl, cmd, NULL)))
6492
        goto out;
6493

6494 6495
    if (vshCommandOptString(cmd, "desturi", &desturi) <= 0 ||
        vshCommandOptString(cmd, "migrateuri", &migrateuri) < 0 ||
6496 6497
        vshCommandOptString(cmd, "dname", &dname) < 0) {
        vshError(ctl, "%s", _("missing argument"));
6498
        goto out;
6499
    }
6500

6501 6502 6503 6504 6505
    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
        vshError(ctl, "%s", _("malformed xml argument"));
        goto out;
    }

6506 6507
    if (vshCommandOptBool (cmd, "live"))
        flags |= VIR_MIGRATE_LIVE;
6508 6509
    if (vshCommandOptBool (cmd, "p2p"))
        flags |= VIR_MIGRATE_PEER2PEER;
C
Chris Lalancette 已提交
6510
    if (vshCommandOptBool (cmd, "tunnelled"))
6511
        flags |= VIR_MIGRATE_TUNNELLED;
C
Chris Lalancette 已提交
6512

C
Chris Lalancette 已提交
6513 6514 6515 6516 6517
    if (vshCommandOptBool (cmd, "persistent"))
        flags |= VIR_MIGRATE_PERSIST_DEST;
    if (vshCommandOptBool (cmd, "undefinesource"))
        flags |= VIR_MIGRATE_UNDEFINE_SOURCE;

6518 6519 6520
    if (vshCommandOptBool (cmd, "suspend"))
        flags |= VIR_MIGRATE_PAUSED;

6521 6522 6523 6524 6525 6526
    if (vshCommandOptBool (cmd, "copy-storage-all"))
        flags |= VIR_MIGRATE_NON_SHARED_DISK;

    if (vshCommandOptBool (cmd, "copy-storage-inc"))
        flags |= VIR_MIGRATE_NON_SHARED_INC;

6527 6528
    if (vshCommandOptBool (cmd, "change-protection"))
        flags |= VIR_MIGRATE_CHANGE_PROTECTION;
6529 6530

    if (xmlfile &&
6531 6532
        virFileReadAll(xmlfile, 8192, &xml) < 0) {
        vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
6533
        goto out;
6534
    }
6535

6536 6537 6538 6539
    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. */
6540

6541
        if (migrateuri != NULL) {
J
Jim Fehlig 已提交
6542
            vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration"));
6543
            goto out;
6544
        }
6545

6546
        if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0)
6547
            ret = '0';
6548 6549 6550 6551 6552 6553
    } else {
        /* For traditional live migration, connect to the destination host directly. */
        virConnectPtr dconn = NULL;
        virDomainPtr ddom = NULL;

        dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0);
6554
        if (!dconn) goto out;
6555

6556
        ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0);
6557 6558
        if (ddom) {
            virDomainFree(ddom);
6559
            ret = '0';
6560 6561 6562
        }
        virConnectClose (dconn);
    }
6563

6564 6565 6566
out:
    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
out_sig:
6567
    if (dom) virDomainFree (dom);
6568
    VIR_FREE(xml);
6569 6570 6571
    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
}

W
Wen Congyang 已提交
6572
static void
6573 6574
print_job_progress(const char *label, unsigned long long remaining,
                   unsigned long long total)
W
Wen Congyang 已提交
6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593
{
    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;
        }
    }

6594 6595
    /* see comments in vshError about why we must flush */
    fflush(stdout);
6596
    fprintf(stderr, "\r%s: [%3d %%]", label, progress);
6597
    fflush(stderr);
W
Wen Congyang 已提交
6598 6599
}

6600 6601 6602 6603 6604 6605 6606 6607 6608 6609
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 已提交
6610
static bool
6611 6612 6613 6614 6615 6616 6617 6618
vshWatchJob(vshControl *ctl,
            virDomainPtr dom,
            bool verbose,
            int pipe_fd,
            int timeout,
            jobWatchTimeoutFunc timeout_func,
            void *opaque,
            const char *label)
6619 6620 6621
{
    struct sigaction sig_action;
    struct sigaction old_sig_action;
6622
    struct pollfd pollfd;
W
Wen Congyang 已提交
6623
    struct timeval start, curr;
6624 6625 6626 6627
    virDomainJobInfo jobinfo;
    int ret = -1;
    char retchar;
    bool functionReturn = false;
6628 6629 6630 6631
    sigset_t sigmask, oldsigmask;

    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
6632 6633 6634 6635 6636 6637 6638

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

6639
    pollfd.fd = pipe_fd;
6640 6641 6642
    pollfd.events = POLLIN;
    pollfd.revents = 0;

W
Wen Congyang 已提交
6643
    GETTIMEOFDAY(&start);
W
Wen Congyang 已提交
6644
    while (1) {
6645
repoll:
W
Wen Congyang 已提交
6646 6647
        ret = poll(&pollfd, 1, 500);
        if (ret > 0) {
6648 6649 6650
            if (pollfd.revents & POLLIN &&
                saferead(pipe_fd, &retchar, sizeof(retchar)) > 0 &&
                retchar == '0') {
W
Wen Congyang 已提交
6651 6652
                    if (verbose) {
                        /* print [100 %] */
6653
                        print_job_progress(label, 0, 1);
W
Wen Congyang 已提交
6654
                    }
6655 6656 6657
                    break;
            }
            goto cleanup;
W
Wen Congyang 已提交
6658 6659 6660 6661 6662 6663 6664 6665 6666 6667
        }

        if (ret < 0) {
            if (errno == EINTR) {
                if (intCaught) {
                    virDomainAbortJob(dom);
                    intCaught = 0;
                } else
                    goto repoll;
            }
6668
            goto cleanup;
W
Wen Congyang 已提交
6669 6670
        }

W
Wen Congyang 已提交
6671 6672 6673 6674
        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. */
6675 6676 6677
            vshDebug(ctl, VSH_ERR_DEBUG, "%s timeout", label);
            if (timeout_func)
                (timeout_func)(ctl, dom, opaque);
W
Wen Congyang 已提交
6678 6679 6680
            timeout = 0;
        }

W
Wen Congyang 已提交
6681 6682 6683 6684 6685
        if (verbose) {
            pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
            ret = virDomainGetJobInfo(dom, &jobinfo);
            pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
            if (ret == 0)
6686
                print_job_progress(label, jobinfo.dataRemaining,
6687
                                   jobinfo.dataTotal);
6688 6689 6690
        }
    }

6691 6692 6693
    functionReturn = true;

cleanup:
6694
    sigaction(SIGINT, &old_sig_action, NULL);
6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 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 6743 6744 6745 6746 6747 6748 6749
    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"));
6750 6751 6752 6753 6754 6755 6756

    virThreadJoin(&workerThread);

cleanup:
    virDomainFree(dom);
    VIR_FORCE_CLOSE(p[0]);
    VIR_FORCE_CLOSE(p[1]);
W
Wen Congyang 已提交
6757
    return functionReturn;
6758 6759
}

6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770
/*
 * "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")},
6771
    {"downtime", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum tolerable downtime (in milliseconds) for migration")},
6772 6773 6774
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6775
static bool
6776 6777 6778
cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
6779
    long long downtime = 0;
E
Eric Blake 已提交
6780
    bool ret = false;
6781

6782
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6783
        return false;
6784 6785

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

6788 6789
    if (vshCommandOptLongLong(cmd, "downtime", &downtime) < 0 ||
        downtime < 1) {
6790 6791 6792 6793 6794 6795 6796
        vshError(ctl, "%s", _("migrate: Invalid downtime"));
        goto done;
    }

    if (virDomainMigrateSetMaxDowntime(dom, downtime, 0))
        goto done;

E
Eric Blake 已提交
6797
    ret = true;
6798 6799 6800 6801 6802 6803

done:
    virDomainFree(dom);
    return ret;
}

6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847
/*
 * "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;
}

6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886
/*
 * "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;
}

6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903
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))
6904
        goto cleanup;
6905 6906

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
6907
        goto cleanup;
6908 6909

    if (vshCommandOptString(cmd, "path", &path) < 0)
6910
        goto cleanup;
6911

6912 6913
    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
        vshError(ctl, "%s", _("bandwidth must be a number"));
6914
        goto cleanup;
6915
    }
6916 6917 6918 6919 6920 6921 6922 6923 6924 6925

    if (mode == VSH_CMD_BLOCK_JOB_ABORT)
        ret = virDomainBlockJobAbort(dom, path, 0);
    else if (mode == VSH_CMD_BLOCK_JOB_INFO)
        ret = virDomainGetBlockJobInfo(dom, path, info, 0);
    else if (mode == VSH_CMD_BLOCK_JOB_SPEED)
        ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
    else if (mode == VSH_CMD_BLOCK_JOB_PULL)
        ret = virDomainBlockPull(dom, path, bandwidth, 0);

6926
cleanup:
6927 6928
    if (dom)
        virDomainFree(dom);
6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967
    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")},
    {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 已提交
6968
    {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Abort the active job on the specified disk")},
6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011
    {"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;
}

7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068
/*
 * "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")},
    {"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 in kilobytes, "
                                           "the size must be integer")},
    {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;
    }

    if (vshCommandOptULongLong(cmd, "size", &size) < 0) {
        vshError(ctl, "%s", _("Unable to parse integer"));
        return false;
    }

    if (size > ULLONG_MAX / 1024) {
        vshError(ctl, _("Size must be less than %llu"), ULLONG_MAX / 1024);
        return false;
    }

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

7070 7071 7072
/*
 * "net-autostart" command
 */
7073
static const vshCmdInfo info_network_autostart[] = {
7074
    {"help", N_("autostart a network")},
7075
    {"desc",
7076
     N_("Configure a network to be automatically started at boot.")},
7077 7078 7079
    {NULL, NULL}
};

7080
static const vshCmdOptDef opts_network_autostart[] = {
7081 7082
    {"network",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
7083 7084 7085
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7086
static bool
7087
cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
7088 7089
{
    virNetworkPtr network;
7090
    const char *name;
7091 7092
    int autostart;

7093
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7094
        return false;
7095

J
Jim Meyering 已提交
7096
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
7097
        return false;
7098 7099 7100 7101

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

    if (virNetworkSetAutostart(network, autostart) < 0) {
7102
        if (autostart)
7103
            vshError(ctl, _("failed to mark network %s as autostarted"), name);
7104
        else
7105
            vshError(ctl, _("failed to unmark network %s as autostarted"), name);
7106
        virNetworkFree(network);
E
Eric Blake 已提交
7107
        return false;
7108 7109
    }

7110
    if (autostart)
7111
        vshPrint(ctl, _("Network %s marked as autostarted\n"), name);
7112
    else
7113
        vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name);
7114

L
Laine Stump 已提交
7115
    virNetworkFree(network);
E
Eric Blake 已提交
7116
    return true;
7117
}
K
Karel Zak 已提交
7118

L
Lei Li 已提交
7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256
/*
 * "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")},
    {"total_bytes_sec", VSH_OT_INT, VSH_OFLAG_NONE,
     N_("total throughput limit in bytes per second")},
    {"read_bytes_sec", VSH_OT_INT, VSH_OFLAG_NONE,
     N_("read throughput limit in bytes per second")},
    {"write_bytes_sec", VSH_OT_INT, VSH_OFLAG_NONE,
     N_("write throughput limit in bytes per second")},
    {"total_iops_sec", VSH_OT_INT, VSH_OFLAG_NONE,
     N_("total I/O operations limit per second")},
    {"read_iops_sec", VSH_OT_INT, VSH_OFLAG_NONE,
     N_("read I/O operations limit per second")},
    {"write_iops_sec", VSH_OT_INT, VSH_OFLAG_NONE,
     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;

    if ((rv = vshCommandOptULongLong(cmd, "total_bytes_sec", &total_bytes_sec)) < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

    if ((rv = vshCommandOptULongLong(cmd, "read_bytes_sec", &read_bytes_sec)) < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

    if ((rv = vshCommandOptULongLong(cmd, "write_bytes_sec", &write_bytes_sec)) < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

    if ((rv = vshCommandOptULongLong(cmd, "total_iops_sec", &total_iops_sec)) < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

    if ((rv = vshCommandOptULongLong(cmd, "read_iops_sec", &read_iops_sec)) < 0) {
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    } else if (rv > 0) {
        nparams++;
    }

    if ((rv = vshCommandOptULongLong(cmd, "write_iops_sec", &write_iops_sec)) < 0) {
        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++) {
7257 7258 7259
            char *str = vshGetTypedParamValue(ctl, &params[i]);
            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
            VIR_FREE(str);
L
Lei Li 已提交
7260 7261
        }

7262
        ret = true;
7263
        goto cleanup;
L
Lei Li 已提交
7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336
    } else {
        /* Set the block I/O throttle, match by opt since parameters can be 0 */
        params = vshCalloc(ctl, nparams, sizeof(*params));
        i = 0;

        if ((i < nparams) && (vshCommandOptBool(cmd, "total_bytes_sec"))) {
            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++;
        }

        if ((i < nparams) && (vshCommandOptBool(cmd, "read_bytes_sec"))) {
            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++;
        }

        if ((i < nparams) && (vshCommandOptBool(cmd, "write_bytes_sec"))) {
            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++;
        }

        if ((i < nparams) && (vshCommandOptBool(cmd, "total_iops_sec"))) {
            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++;
        }

        if ((i < nparams) && (vshCommandOptBool(cmd, "read_iops_sec"))) {
            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++;
        }

        if ((i < nparams) && (vshCommandOptBool(cmd, "write_iops_sec"))) {
            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;
}

7337 7338 7339
/*
 * "net-create" command
 */
7340
static const vshCmdInfo info_network_create[] = {
7341 7342
    {"help", N_("create a network from an XML file")},
    {"desc", N_("Create a network.")},
7343 7344 7345
    {NULL, NULL}
};

7346
static const vshCmdOptDef opts_network_create[] = {
7347
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
7348 7349 7350
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7351
static bool
7352
cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
7353 7354
{
    virNetworkPtr network;
7355
    const char *from = NULL;
E
Eric Blake 已提交
7356
    bool ret = true;
7357
    char *buffer;
7358

7359
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7360
        return false;
7361

7362
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
7363
        return false;
7364

7365
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
7366
        return false;
7367 7368

    network = virNetworkCreateXML(ctl->conn, buffer);
7369
    VIR_FREE(buffer);
7370

7371 7372 7373
    if (network != NULL) {
        vshPrint(ctl, _("Network %s created from %s\n"),
                 virNetworkGetName(network), from);
L
Laine Stump 已提交
7374
        virNetworkFree(network);
7375
    } else {
7376
        vshError(ctl, _("Failed to create network from %s"), from);
E
Eric Blake 已提交
7377
        ret = false;
7378 7379 7380 7381 7382 7383 7384 7385
    }
    return ret;
}


/*
 * "net-define" command
 */
7386
static const vshCmdInfo info_network_define[] = {
7387 7388
    {"help", N_("define (but don't start) a network from an XML file")},
    {"desc", N_("Define a network.")},
7389 7390 7391
    {NULL, NULL}
};

7392
static const vshCmdOptDef opts_network_define[] = {
7393
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
7394 7395 7396
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7397
static bool
7398
cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
7399 7400
{
    virNetworkPtr network;
7401
    const char *from = NULL;
E
Eric Blake 已提交
7402
    bool ret = true;
7403
    char *buffer;
7404

7405
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7406
        return false;
7407

7408
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
7409
        return false;
7410

7411
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
7412
        return false;
7413 7414

    network = virNetworkDefineXML(ctl->conn, buffer);
7415
    VIR_FREE(buffer);
7416

7417 7418 7419
    if (network != NULL) {
        vshPrint(ctl, _("Network %s defined from %s\n"),
                 virNetworkGetName(network), from);
L
Laine Stump 已提交
7420
        virNetworkFree(network);
7421
    } else {
7422
        vshError(ctl, _("Failed to define network from %s"), from);
E
Eric Blake 已提交
7423
        ret = false;
7424 7425 7426 7427 7428 7429 7430 7431
    }
    return ret;
}


/*
 * "net-destroy" command
 */
7432
static const vshCmdInfo info_network_destroy[] = {
7433 7434
    {"help", N_("destroy (stop) a network")},
    {"desc", N_("Forcefully stop a given network.")},
7435 7436 7437
    {NULL, NULL}
};

7438
static const vshCmdOptDef opts_network_destroy[] = {
7439
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
7440 7441 7442
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7443
static bool
7444
cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
7445 7446
{
    virNetworkPtr network;
E
Eric Blake 已提交
7447
    bool ret = true;
7448
    const char *name;
7449

7450
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7451
        return false;
7452

J
Jim Meyering 已提交
7453
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
7454
        return false;
7455 7456 7457 7458

    if (virNetworkDestroy(network) == 0) {
        vshPrint(ctl, _("Network %s destroyed\n"), name);
    } else {
7459
        vshError(ctl, _("Failed to destroy network %s"), name);
E
Eric Blake 已提交
7460
        ret = false;
7461 7462
    }

7463
    virNetworkFree(network);
7464 7465 7466 7467 7468 7469 7470
    return ret;
}


/*
 * "net-dumpxml" command
 */
7471
static const vshCmdInfo info_network_dumpxml[] = {
7472 7473
    {"help", N_("network information in XML")},
    {"desc", N_("Output the network information as an XML dump to stdout.")},
7474 7475 7476
    {NULL, NULL}
};

7477
static const vshCmdOptDef opts_network_dumpxml[] = {
7478
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
7479
    {"inactive", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("network information of an inactive domain")},
7480 7481 7482
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7483
static bool
7484
cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
7485 7486
{
    virNetworkPtr network;
E
Eric Blake 已提交
7487
    bool ret = true;
7488
    char *dump;
7489 7490
    unsigned int flags = 0;
    int inactive;
7491

7492
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7493
        return false;
7494

J
Jim Meyering 已提交
7495
    if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
E
Eric Blake 已提交
7496
        return false;
7497

7498 7499 7500 7501 7502 7503
    inactive = vshCommandOptBool (cmd, "inactive");
    if (inactive)
        flags |= VIR_NETWORK_XML_INACTIVE;

    dump = virNetworkGetXMLDesc(network, flags);

7504
    if (dump != NULL) {
7505
        vshPrint(ctl, "%s", dump);
7506
        VIR_FREE(dump);
7507
    } else {
E
Eric Blake 已提交
7508
        ret = false;
7509 7510 7511 7512 7513 7514
    }

    virNetworkFree(network);
    return ret;
}

O
Osier Yang 已提交
7515 7516 7517 7518 7519
/*
 * "net-info" command
 */
static const vshCmdInfo info_network_info[] = {
    {"help", N_("network information")},
E
Eric Blake 已提交
7520
    {"desc", N_("Returns basic information about the network")},
O
Osier Yang 已提交
7521 7522 7523 7524 7525 7526 7527 7528
    {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 已提交
7529
static bool
O
Osier Yang 已提交
7530 7531 7532 7533 7534 7535 7536 7537 7538 7539
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 已提交
7540
        return false;
O
Osier Yang 已提交
7541 7542 7543

    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
                                           VSH_BYNAME)))
E
Eric Blake 已提交
7544
        return false;
O
Osier Yang 已提交
7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569

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

7570
    VIR_FREE(bridge);
O
Osier Yang 已提交
7571
    virNetworkFree(network);
E
Eric Blake 已提交
7572
    return true;
O
Osier Yang 已提交
7573
}
7574

7575 7576 7577 7578
/*
 * "iface-edit" command
 */
static const vshCmdInfo info_interface_edit[] = {
7579 7580
    {"help", N_("edit XML configuration for a physical host interface")},
    {"desc", N_("Edit the XML configuration for a physical host interface.")},
7581 7582 7583 7584
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_edit[] = {
7585
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
7586 7587 7588
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7589
static bool
7590 7591
cmdInterfaceEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
7592
    bool ret = false;
7593 7594 7595 7596 7597
    virInterfacePtr iface = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;
E
Eric Blake 已提交
7598
    unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
7599

7600
    if (!vshConnectionUsability(ctl, ctl->conn))
7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626
        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 已提交
7627
        ret = true;
7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639
        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)) {
7640 7641
        vshError(ctl, "%s",
                 _("ERROR: the XML configuration was changed by another user"));
7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653
        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 已提交
7654
    ret = true;
7655 7656 7657 7658 7659

cleanup:
    if (iface)
        virInterfaceFree (iface);

7660 7661 7662
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);
7663 7664 7665

    if (tmp) {
        unlink (tmp);
7666
        VIR_FREE(tmp);
7667 7668 7669 7670 7671
    }

    return ret;
}

7672 7673 7674
/*
 * "net-list" command
 */
7675
static const vshCmdInfo info_network_list[] = {
7676 7677
    {"help", N_("list networks")},
    {"desc", N_("Returns list of networks.")},
7678 7679 7680
    {NULL, NULL}
};

7681
static const vshCmdOptDef opts_network_list[] = {
7682 7683
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")},
7684 7685 7686
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7687
static bool
7688
cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
7689 7690 7691 7692 7693
{
    int inactive = vshCommandOptBool(cmd, "inactive");
    int all = vshCommandOptBool(cmd, "all");
    int active = !inactive || all ? 1 : 0;
    int maxactive = 0, maxinactive = 0, i;
7694
    char **activeNames = NULL, **inactiveNames = NULL;
7695 7696
    inactive |= all;

7697
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7698
        return false;
7699 7700

    if (active) {
7701 7702
        maxactive = virConnectNumOfNetworks(ctl->conn);
        if (maxactive < 0) {
7703
            vshError(ctl, "%s", _("Failed to list active networks"));
E
Eric Blake 已提交
7704
            return false;
7705
        }
7706
        if (maxactive) {
7707
            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
7708

7709
            if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
7710
                                                    maxactive)) < 0) {
7711
                vshError(ctl, "%s", _("Failed to list active networks"));
7712
                VIR_FREE(activeNames);
E
Eric Blake 已提交
7713
                return false;
7714
            }
7715

7716
            qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
7717
        }
7718 7719
    }
    if (inactive) {
7720 7721
        maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
        if (maxinactive < 0) {
7722
            vshError(ctl, "%s", _("Failed to list inactive networks"));
7723
            VIR_FREE(activeNames);
E
Eric Blake 已提交
7724
            return false;
7725
        }
7726 7727 7728
        if (maxinactive) {
            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);

7729 7730 7731
            if ((maxinactive =
                     virConnectListDefinedNetworks(ctl->conn, inactiveNames,
                                                   maxinactive)) < 0) {
7732
                vshError(ctl, "%s", _("Failed to list inactive networks"));
7733 7734
                VIR_FREE(activeNames);
                VIR_FREE(inactiveNames);
E
Eric Blake 已提交
7735
                return false;
7736
            }
7737

7738 7739
            qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
        }
7740
    }
7741 7742
    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
                  _("Autostart"));
7743
    vshPrintExtra(ctl, "-----------------------------------------\n");
7744 7745

    for (i = 0; i < maxactive; i++) {
7746 7747
        virNetworkPtr network =
            virNetworkLookupByName(ctl->conn, activeNames[i]);
7748 7749
        const char *autostartStr;
        int autostart = 0;
7750 7751 7752

        /* this kind of work with networks is not atomic operation */
        if (!network) {
7753
            VIR_FREE(activeNames[i]);
7754
            continue;
7755
        }
7756

7757 7758 7759
        if (virNetworkGetAutostart(network, &autostart) < 0)
            autostartStr = _("no autostart");
        else
7760
            autostartStr = autostart ? _("yes") : _("no");
7761 7762 7763 7764 7765

        vshPrint(ctl, "%-20s %-10s %-10s\n",
                 virNetworkGetName(network),
                 _("active"),
                 autostartStr);
7766
        virNetworkFree(network);
7767
        VIR_FREE(activeNames[i]);
7768 7769 7770
    }
    for (i = 0; i < maxinactive; i++) {
        virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
7771 7772
        const char *autostartStr;
        int autostart = 0;
7773 7774 7775

        /* this kind of work with networks is not atomic operation */
        if (!network) {
7776
            VIR_FREE(inactiveNames[i]);
7777
            continue;
7778
        }
7779

7780 7781 7782
        if (virNetworkGetAutostart(network, &autostart) < 0)
            autostartStr = _("no autostart");
        else
7783
            autostartStr = autostart ? _("yes") : _("no");
7784

7785
        vshPrint(ctl, "%-20s %-10s %-10s\n",
7786 7787 7788
                 inactiveNames[i],
                 _("inactive"),
                 autostartStr);
7789 7790

        virNetworkFree(network);
7791
        VIR_FREE(inactiveNames[i]);
7792
    }
7793 7794
    VIR_FREE(activeNames);
    VIR_FREE(inactiveNames);
E
Eric Blake 已提交
7795
    return true;
7796 7797 7798 7799 7800 7801
}


/*
 * "net-name" command
 */
7802
static const vshCmdInfo info_network_name[] = {
7803
    {"help", N_("convert a network UUID to network name")},
7804
    {"desc", ""},
7805 7806 7807
    {NULL, NULL}
};

7808
static const vshCmdOptDef opts_network_name[] = {
7809
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")},
7810 7811 7812
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7813
static bool
7814
cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
7815 7816 7817
{
    virNetworkPtr network;

7818
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7819
        return false;
J
Jim Meyering 已提交
7820
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
7821
                                           VSH_BYUUID)))
E
Eric Blake 已提交
7822
        return false;
7823 7824 7825

    vshPrint(ctl, "%s\n", virNetworkGetName(network));
    virNetworkFree(network);
E
Eric Blake 已提交
7826
    return true;
7827 7828 7829 7830 7831 7832
}


/*
 * "net-start" command
 */
7833
static const vshCmdInfo info_network_start[] = {
7834 7835
    {"help", N_("start a (previously defined) inactive network")},
    {"desc", N_("Start a network.")},
7836 7837 7838
    {NULL, NULL}
};

7839
static const vshCmdOptDef opts_network_start[] = {
7840
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive network")},
7841 7842 7843
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7844
static bool
7845
cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
7846 7847
{
    virNetworkPtr network;
E
Eric Blake 已提交
7848
    bool ret = true;
7849

7850
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7851
        return false;
7852

J
Jim Meyering 已提交
7853
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL, VSH_BYNAME)))
E
Eric Blake 已提交
7854
         return false;
7855 7856 7857

    if (virNetworkCreate(network) == 0) {
        vshPrint(ctl, _("Network %s started\n"),
7858
                 virNetworkGetName(network));
7859
    } else {
7860
        vshError(ctl, _("Failed to start network %s"),
7861
                 virNetworkGetName(network));
E
Eric Blake 已提交
7862
        ret = false;
7863
    }
L
Laine Stump 已提交
7864
    virNetworkFree(network);
7865 7866 7867 7868 7869 7870 7871
    return ret;
}


/*
 * "net-undefine" command
 */
7872
static const vshCmdInfo info_network_undefine[] = {
7873 7874
    {"help", N_("undefine an inactive network")},
    {"desc", N_("Undefine the configuration for an inactive network.")},
7875 7876 7877
    {NULL, NULL}
};

7878
static const vshCmdOptDef opts_network_undefine[] = {
7879
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
7880 7881 7882
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7883
static bool
7884
cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
7885 7886
{
    virNetworkPtr network;
E
Eric Blake 已提交
7887
    bool ret = true;
7888
    const char *name;
7889

7890
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7891
        return false;
7892

J
Jim Meyering 已提交
7893
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
7894
        return false;
7895 7896 7897 7898

    if (virNetworkUndefine(network) == 0) {
        vshPrint(ctl, _("Network %s has been undefined\n"), name);
    } else {
7899
        vshError(ctl, _("Failed to undefine network %s"), name);
E
Eric Blake 已提交
7900
        ret = false;
7901 7902
    }

L
Laine Stump 已提交
7903
    virNetworkFree(network);
7904 7905 7906 7907 7908 7909 7910
    return ret;
}


/*
 * "net-uuid" command
 */
7911
static const vshCmdInfo info_network_uuid[] = {
7912
    {"help", N_("convert a network name to network UUID")},
7913
    {"desc", ""},
7914 7915 7916
    {NULL, NULL}
};

7917
static const vshCmdOptDef opts_network_uuid[] = {
7918
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
7919 7920 7921
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7922
static bool
7923
cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
7924 7925 7926 7927
{
    virNetworkPtr network;
    char uuid[VIR_UUID_STRING_BUFLEN];

7928
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7929
        return false;
7930

J
Jim Meyering 已提交
7931
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
7932
                                           VSH_BYNAME)))
E
Eric Blake 已提交
7933
        return false;
7934 7935 7936 7937

    if (virNetworkGetUUIDString(network, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
7938
        vshError(ctl, "%s", _("failed to get network UUID"));
7939

L
Laine Stump 已提交
7940
    virNetworkFree(network);
E
Eric Blake 已提交
7941
    return true;
7942 7943 7944
}


7945 7946 7947 7948 7949
/**************************************************************************/
/*
 * "iface-list" command
 */
static const vshCmdInfo info_interface_list[] = {
7950 7951
    {"help", N_("list physical host interfaces")},
    {"desc", N_("Returns list of physical host interfaces.")},
7952 7953 7954 7955
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_list[] = {
7956 7957
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive interfaces")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")},
7958 7959
    {NULL, 0, 0, NULL}
};
E
Eric Blake 已提交
7960
static bool
7961 7962 7963 7964 7965 7966 7967 7968 7969
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;

7970
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7971
        return false;
7972 7973 7974 7975

    if (active) {
        maxactive = virConnectNumOfInterfaces(ctl->conn);
        if (maxactive < 0) {
7976
            vshError(ctl, "%s", _("Failed to list active interfaces"));
E
Eric Blake 已提交
7977
            return false;
7978 7979 7980 7981 7982 7983
        }
        if (maxactive) {
            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);

            if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames,
                                                    maxactive)) < 0) {
7984
                vshError(ctl, "%s", _("Failed to list active interfaces"));
7985
                VIR_FREE(activeNames);
E
Eric Blake 已提交
7986
                return false;
7987 7988 7989 7990 7991 7992 7993 7994
            }

            qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
        }
    }
    if (inactive) {
        maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn);
        if (maxinactive < 0) {
7995
            vshError(ctl, "%s", _("Failed to list inactive interfaces"));
7996
            VIR_FREE(activeNames);
E
Eric Blake 已提交
7997
            return false;
7998 7999 8000 8001
        }
        if (maxinactive) {
            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);

8002 8003 8004
            if ((maxinactive =
                     virConnectListDefinedInterfaces(ctl->conn, inactiveNames,
                                                     maxinactive)) < 0) {
8005
                vshError(ctl, "%s", _("Failed to list inactive interfaces"));
8006 8007
                VIR_FREE(activeNames);
                VIR_FREE(inactiveNames);
E
Eric Blake 已提交
8008
                return false;
8009 8010 8011 8012 8013
            }

            qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
        }
    }
8014 8015
    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
                  _("MAC Address"));
8016 8017 8018
    vshPrintExtra(ctl, "--------------------------------------------\n");

    for (i = 0; i < maxactive; i++) {
8019 8020
        virInterfacePtr iface =
            virInterfaceLookupByName(ctl->conn, activeNames[i]);
8021 8022 8023

        /* this kind of work with interfaces is not atomic */
        if (!iface) {
8024
            VIR_FREE(activeNames[i]);
8025 8026 8027 8028 8029 8030 8031 8032
            continue;
        }

        vshPrint(ctl, "%-20s %-10s %s\n",
                 virInterfaceGetName(iface),
                 _("active"),
                 virInterfaceGetMACString(iface));
        virInterfaceFree(iface);
8033
        VIR_FREE(activeNames[i]);
8034 8035
    }
    for (i = 0; i < maxinactive; i++) {
8036 8037
        virInterfacePtr iface =
            virInterfaceLookupByName(ctl->conn, inactiveNames[i]);
8038 8039 8040

        /* this kind of work with interfaces is not atomic */
        if (!iface) {
8041
            VIR_FREE(inactiveNames[i]);
8042 8043 8044 8045 8046 8047 8048 8049
            continue;
        }

        vshPrint(ctl, "%-20s %-10s %s\n",
                 virInterfaceGetName(iface),
                 _("inactive"),
                 virInterfaceGetMACString(iface));
        virInterfaceFree(iface);
8050
        VIR_FREE(inactiveNames[i]);
8051
    }
8052 8053
    VIR_FREE(activeNames);
    VIR_FREE(inactiveNames);
E
Eric Blake 已提交
8054
    return true;
8055 8056 8057 8058 8059 8060 8061

}

/*
 * "iface-name" command
 */
static const vshCmdInfo info_interface_name[] = {
8062
    {"help", N_("convert an interface MAC address to interface name")},
8063 8064 8065 8066 8067
    {"desc", ""},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_name[] = {
8068
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface mac")},
8069 8070 8071
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8072
static bool
8073 8074 8075 8076
cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;

8077
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8078
        return false;
8079
    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
8080
                                           VSH_BYMAC)))
E
Eric Blake 已提交
8081
        return false;
8082 8083 8084

    vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
    virInterfaceFree(iface);
E
Eric Blake 已提交
8085
    return true;
8086 8087 8088 8089 8090 8091
}

/*
 * "iface-mac" command
 */
static const vshCmdInfo info_interface_mac[] = {
8092
    {"help", N_("convert an interface name to interface MAC address")},
8093 8094 8095 8096 8097
    {"desc", ""},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_mac[] = {
8098
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
8099 8100 8101
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8102
static bool
8103 8104 8105 8106
cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;

8107
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8108
        return false;
8109
    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
8110
                                           VSH_BYNAME)))
E
Eric Blake 已提交
8111
        return false;
8112 8113 8114

    vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
    virInterfaceFree(iface);
E
Eric Blake 已提交
8115
    return true;
8116 8117 8118 8119 8120 8121
}

/*
 * "iface-dumpxml" command
 */
static const vshCmdInfo info_interface_dumpxml[] = {
8122 8123
    {"help", N_("interface information in XML")},
    {"desc", N_("Output the physical host interface information as an XML dump to stdout.")},
8124 8125 8126 8127
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_dumpxml[] = {
8128 8129
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
8130 8131 8132
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8133
static bool
8134 8135 8136
cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8137
    bool ret = true;
8138
    char *dump;
E
Eric Blake 已提交
8139
    unsigned int flags = 0;
8140 8141 8142 8143
    int inactive = vshCommandOptBool(cmd, "inactive");

    if (inactive)
        flags |= VIR_INTERFACE_XML_INACTIVE;
8144

8145
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8146
        return false;
8147 8148

    if (!(iface = vshCommandOptInterface(ctl, cmd, NULL)))
E
Eric Blake 已提交
8149
        return false;
8150

8151
    dump = virInterfaceGetXMLDesc(iface, flags);
8152
    if (dump != NULL) {
8153
        vshPrint(ctl, "%s", dump);
8154
        VIR_FREE(dump);
8155
    } else {
E
Eric Blake 已提交
8156
        ret = false;
8157 8158 8159 8160 8161 8162 8163 8164 8165 8166
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-define" command
 */
static const vshCmdInfo info_interface_define[] = {
8167 8168
    {"help", N_("define (but don't start) a physical host interface from an XML file")},
    {"desc", N_("Define a physical host interface.")},
8169 8170 8171 8172
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_define[] = {
8173
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML interface description")},
8174 8175 8176
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8177
static bool
8178 8179
cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
{
8180
    virInterfacePtr iface;
8181
    const char *from = NULL;
E
Eric Blake 已提交
8182
    bool ret = true;
8183 8184
    char *buffer;

8185
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8186
        return false;
8187

8188
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
8189
        return false;
8190 8191

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

8194
    iface = virInterfaceDefineXML(ctl->conn, buffer, 0);
8195
    VIR_FREE(buffer);
8196

8197
    if (iface != NULL) {
8198
        vshPrint(ctl, _("Interface %s defined from %s\n"),
8199
                 virInterfaceGetName(iface), from);
L
Laine Stump 已提交
8200
        virInterfaceFree (iface);
8201
    } else {
8202
        vshError(ctl, _("Failed to define interface from %s"), from);
E
Eric Blake 已提交
8203
        ret = false;
8204 8205 8206 8207 8208 8209 8210 8211
    }
    return ret;
}

/*
 * "iface-undefine" command
 */
static const vshCmdInfo info_interface_undefine[] = {
8212 8213
    {"help", N_("undefine a physical host interface (remove it from configuration)")},
    {"desc", N_("undefine an interface.")},
8214 8215 8216 8217
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_undefine[] = {
8218
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
8219 8220 8221
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8222
static bool
8223 8224 8225
cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8226
    bool ret = true;
8227
    const char *name;
8228

8229
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8230
        return false;
8231 8232

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
8233
        return false;
8234 8235 8236 8237

    if (virInterfaceUndefine(iface) == 0) {
        vshPrint(ctl, _("Interface %s undefined\n"), name);
    } else {
8238
        vshError(ctl, _("Failed to undefine interface %s"), name);
E
Eric Blake 已提交
8239
        ret = false;
8240 8241 8242 8243 8244 8245 8246 8247 8248 8249
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-start" command
 */
static const vshCmdInfo info_interface_start[] = {
8250 8251
    {"help", N_("start a physical host interface (enable it / \"if-up\")")},
    {"desc", N_("start a physical host interface.")},
8252 8253 8254 8255
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_start[] = {
8256
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
8257 8258 8259
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8260
static bool
8261 8262 8263
cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8264
    bool ret = true;
8265
    const char *name;
8266

8267
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8268
        return false;
8269 8270

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
8271
        return false;
8272 8273 8274 8275

    if (virInterfaceCreate(iface, 0) == 0) {
        vshPrint(ctl, _("Interface %s started\n"), name);
    } else {
8276
        vshError(ctl, _("Failed to start interface %s"), name);
E
Eric Blake 已提交
8277
        ret = false;
8278 8279 8280 8281 8282 8283 8284 8285 8286 8287
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-destroy" command
 */
static const vshCmdInfo info_interface_destroy[] = {
8288
    {"help", N_("destroy a physical host interface (disable it / \"if-down\")")},
8289
    {"desc", N_("forcefully stop a physical host interface.")},
8290 8291 8292 8293
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_destroy[] = {
8294
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
8295 8296 8297
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8298
static bool
8299 8300 8301
cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
8302
    bool ret = true;
8303
    const char *name;
8304

8305
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8306
        return false;
8307 8308

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
8309
        return false;
8310 8311 8312 8313

    if (virInterfaceDestroy(iface, 0) == 0) {
        vshPrint(ctl, _("Interface %s destroyed\n"), name);
    } else {
8314
        vshError(ctl, _("Failed to destroy interface %s"), name);
E
Eric Blake 已提交
8315
        ret = false;
8316 8317 8318 8319 8320 8321
    }

    virInterfaceFree(iface);
    return ret;
}

8322 8323 8324 8325 8326
/*
 * "iface-begin" command
 */
static const vshCmdInfo info_interface_begin[] = {
    {"help", N_("create a snapshot of current interfaces settings, "
Y
Yuri Chornoivan 已提交
8327
                "which can be later committed (iface-commit) or "
8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406
                "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;
}
8407

8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709
/*
 * "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"),
             if_name, br_name);

    /* 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 已提交
8710
        vshError(ctl, "%s", _("Multiple interfaces attached to bridge"));
8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820
        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;
}

8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834
/*
 * "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 已提交
8835
static bool
8836 8837 8838
cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
8839
    const char *from = NULL;
E
Eric Blake 已提交
8840
    bool ret = true;
8841 8842
    char *buffer;

8843
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8844
        return false;
8845

8846
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
8847
        return false;
8848 8849

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
8850
        return false;
8851 8852 8853 8854 8855 8856 8857 8858 8859 8860

    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 已提交
8861
        ret = false;
8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880
    }
    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 已提交
8881
static bool
8882 8883 8884
cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
E
Eric Blake 已提交
8885
    bool ret = true;
8886
    const char *name;
8887

8888
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8889
        return false;
8890 8891

    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name)))
E
Eric Blake 已提交
8892
        return false;
8893 8894 8895 8896 8897

    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 已提交
8898
        ret = false;
8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919
    }

    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 已提交
8920
static bool
8921 8922 8923
cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
E
Eric Blake 已提交
8924
    bool ret = true;
8925 8926
    char *dump;

8927
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8928
        return false;
8929 8930

    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL)))
E
Eric Blake 已提交
8931
        return false;
8932 8933 8934

    dump = virNWFilterGetXMLDesc(nwfilter, 0);
    if (dump != NULL) {
8935
        vshPrint(ctl, "%s", dump);
8936 8937
        VIR_FREE(dump);
    } else {
E
Eric Blake 已提交
8938
        ret = false;
8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957
    }

    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 已提交
8958
static bool
8959 8960 8961 8962 8963 8964
cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    int numfilters, i;
    char **names;
    char uuid[VIR_UUID_STRING_BUFLEN];

8965
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8966
        return false;
8967 8968 8969 8970

    numfilters = virConnectNumOfNWFilters(ctl->conn);
    if (numfilters < 0) {
        vshError(ctl, "%s", _("Failed to list network filters"));
E
Eric Blake 已提交
8971
        return false;
8972 8973 8974 8975 8976 8977 8978 8979
    }

    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 已提交
8980
        return false;
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
    }

    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 已提交
9008
    return true;
9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025
}


/*
 * "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 已提交
9026
static bool
9027 9028
cmdNWFilterEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
9029
    bool ret = false;
9030 9031 9032 9033 9034 9035
    virNWFilterPtr nwfilter = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;

9036
    if (!vshConnectionUsability(ctl, ctl->conn))
9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062
        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 已提交
9063
        ret = true;
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
        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 已提交
9090
    ret = true;
9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108

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


9109
/**************************************************************************/
9110
/*
9111
 * "pool-autostart" command
9112
 */
9113
static const vshCmdInfo info_pool_autostart[] = {
9114
    {"help", N_("autostart a pool")},
9115
    {"desc",
9116
     N_("Configure a pool to be automatically started at boot.")},
9117
    {NULL, NULL}
9118 9119
};

9120
static const vshCmdOptDef opts_pool_autostart[] = {
9121 9122
    {"pool",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
9123 9124
    {NULL, 0, 0, NULL}
};
9125

E
Eric Blake 已提交
9126
static bool
9127
cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd)
9128
{
9129
    virStoragePoolPtr pool;
9130
    const char *name;
9131
    int autostart;
9132

9133
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9134
        return false;
9135

9136
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
9137
        return false;
9138

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

9141 9142
    if (virStoragePoolSetAutostart(pool, autostart) < 0) {
        if (autostart)
9143
            vshError(ctl, _("failed to mark pool %s as autostarted"), name);
9144
        else
9145
            vshError(ctl, _("failed to unmark pool %s as autostarted"), name);
9146
        virStoragePoolFree(pool);
E
Eric Blake 已提交
9147
        return false;
9148 9149
    }

9150
    if (autostart)
9151
        vshPrint(ctl, _("Pool %s marked as autostarted\n"), name);
9152
    else
9153
        vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name);
9154

L
Laine Stump 已提交
9155
    virStoragePoolFree(pool);
E
Eric Blake 已提交
9156
    return true;
9157 9158
}

9159
/*
9160
 * "pool-create" command
9161
 */
9162
static const vshCmdInfo info_pool_create[] = {
9163 9164
    {"help", N_("create a pool from an XML file")},
    {"desc", N_("Create a pool.")},
9165 9166 9167
    {NULL, NULL}
};

9168
static const vshCmdOptDef opts_pool_create[] = {
J
Jim Meyering 已提交
9169
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
9170
     N_("file containing an XML pool description")},
9171 9172 9173
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9174
static bool
9175
cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
9176
{
9177
    virStoragePoolPtr pool;
9178
    const char *from = NULL;
E
Eric Blake 已提交
9179
    bool ret = true;
9180
    char *buffer;
9181

9182
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9183
        return false;
9184

9185
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
9186
        return false;
9187

9188
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
9189
        return false;
9190

9191
    pool = virStoragePoolCreateXML(ctl->conn, buffer, 0);
9192
    VIR_FREE(buffer);
9193 9194 9195 9196

    if (pool != NULL) {
        vshPrint(ctl, _("Pool %s created from %s\n"),
                 virStoragePoolGetName(pool), from);
L
Laine Stump 已提交
9197
        virStoragePoolFree(pool);
9198
    } else {
9199
        vshError(ctl, _("Failed to create pool from %s"), from);
E
Eric Blake 已提交
9200
        ret = false;
9201 9202
    }
    return ret;
9203 9204
}

9205

9206 9207 9208 9209
/*
 * "nodedev-create" command
 */
static const vshCmdInfo info_node_device_create[] = {
9210
    {"help", N_("create a device defined "
9211
                          "by an XML file on the node")},
9212
    {"desc", N_("Create a device on the node.  Note that this "
9213 9214 9215 9216 9217 9218 9219
                          "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,
9220
     N_("file containing an XML description of the device")},
9221 9222 9223
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9224
static bool
9225 9226 9227
cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
{
    virNodeDevicePtr dev = NULL;
9228
    const char *from = NULL;
E
Eric Blake 已提交
9229
    bool ret = true;
9230 9231
    char *buffer;

9232
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9233
        return false;
9234

9235
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
9236
        return false;
9237

9238
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
9239
        return false;
9240 9241

    dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
9242
    VIR_FREE(buffer);
9243 9244 9245 9246

    if (dev != NULL) {
        vshPrint(ctl, _("Node device %s created from %s\n"),
                 virNodeDeviceGetName(dev), from);
L
Laine Stump 已提交
9247
        virNodeDeviceFree(dev);
9248
    } else {
9249
        vshError(ctl, _("Failed to create node device from %s"), from);
E
Eric Blake 已提交
9250
        ret = false;
9251 9252 9253 9254 9255 9256 9257 9258 9259 9260
    }

    return ret;
}


/*
 * "nodedev-destroy" command
 */
static const vshCmdInfo info_node_device_destroy[] = {
9261
    {"help", N_("destroy (stop) a device on the node")},
9262
    {"desc", N_("Destroy a device on the node.  Note that this "
9263
                "command destroys devices on the physical host")},
9264 9265 9266 9267 9268
    {NULL, NULL}
};

static const vshCmdOptDef opts_node_device_destroy[] = {
    {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
9269
     N_("name of the device to be destroyed")},
9270 9271 9272
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9273
static bool
9274 9275 9276
cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
{
    virNodeDevicePtr dev = NULL;
E
Eric Blake 已提交
9277
    bool ret = true;
9278
    const char *name = NULL;
9279

9280
    if (!vshConnectionUsability(ctl, ctl->conn)) {
E
Eric Blake 已提交
9281
        return false;
9282 9283
    }

9284
    if (vshCommandOptString(cmd, "name", &name) <= 0)
E
Eric Blake 已提交
9285
        return false;
9286 9287 9288 9289 9290 9291

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

    if (virNodeDeviceDestroy(dev) == 0) {
        vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
    } else {
9292
        vshError(ctl, _("Failed to destroy node device '%s'"), name);
E
Eric Blake 已提交
9293
        ret = false;
9294 9295 9296 9297 9298 9299 9300
    }

    virNodeDeviceFree(dev);
    return ret;
}


9301
/*
9302
 * XML Building helper for pool-define-as and pool-create-as
9303
 */
9304
static const vshCmdOptDef opts_pool_X_as[] = {
9305 9306 9307 9308 9309 9310 9311 9312
    {"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")},
9313
    {"source-format", VSH_OT_STRING, 0, N_("format for underlying storage")},
9314 9315 9316
    {NULL, 0, 0, NULL}
};

9317
static int buildPoolXML(const vshCmd *cmd, const char **retname, char **xml) {
9318

9319 9320
    const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL,
               *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL;
9321
    virBuffer buf = VIR_BUFFER_INITIALIZER;
9322

9323
    if (vshCommandOptString(cmd, "name", &name) <= 0)
9324
        goto cleanup;
9325
    if (vshCommandOptString(cmd, "type", &type) <= 0)
9326 9327
        goto cleanup;

9328 9329 9330 9331 9332
    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 ||
9333 9334
        vshCommandOptString(cmd, "target", &target) < 0) {
        vshError(NULL, "%s", _("missing argument"));
9335
        goto cleanup;
9336
    }
9337

9338 9339
    virBufferAsprintf(&buf, "<pool type='%s'>\n", type);
    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
9340
    if (srcHost || srcPath || srcDev || srcFormat || srcName) {
9341
        virBufferAddLit(&buf, "  <source>\n");
9342

9343
        if (srcHost)
9344
            virBufferAsprintf(&buf, "    <host name='%s'/>\n", srcHost);
9345
        if (srcPath)
9346
            virBufferAsprintf(&buf, "    <dir path='%s'/>\n", srcPath);
9347
        if (srcDev)
9348
            virBufferAsprintf(&buf, "    <device path='%s'/>\n", srcDev);
9349
        if (srcFormat)
9350
            virBufferAsprintf(&buf, "    <format type='%s'/>\n", srcFormat);
9351
        if (srcName)
9352
            virBufferAsprintf(&buf, "    <name>%s</name>\n", srcName);
9353 9354

        virBufferAddLit(&buf, "  </source>\n");
9355 9356
    }
    if (target) {
9357
        virBufferAddLit(&buf, "  <target>\n");
9358
        virBufferAsprintf(&buf, "    <path>%s</path>\n", target);
9359
        virBufferAddLit(&buf, "  </target>\n");
9360
    }
9361 9362 9363 9364
    virBufferAddLit(&buf, "</pool>\n");

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
9365
        return false;
9366
    }
9367 9368 9369

    *xml = virBufferContentAndReset(&buf);
    *retname = name;
E
Eric Blake 已提交
9370
    return true;
9371 9372

cleanup:
9373
    virBufferFreeAndReset(&buf);
E
Eric Blake 已提交
9374
    return false;
9375 9376 9377 9378 9379 9380
}

/*
 * "pool-create-as" command
 */
static const vshCmdInfo info_pool_create_as[] = {
9381 9382
    {"help", N_("create a pool from a set of args")},
    {"desc", N_("Create a pool.")},
9383 9384 9385
    {NULL, NULL}
};

E
Eric Blake 已提交
9386
static bool
9387 9388 9389
cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool;
9390 9391
    const char *name;
    char *xml;
9392
    int printXML = vshCommandOptBool(cmd, "print-xml");
9393

9394
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9395
        return false;
9396 9397

    if (!buildPoolXML(cmd, &name, &xml))
E
Eric Blake 已提交
9398
        return false;
9399

9400
    if (printXML) {
9401
        vshPrint(ctl, "%s", xml);
9402
        VIR_FREE(xml);
9403
    } else {
9404
        pool = virStoragePoolCreateXML(ctl->conn, xml, 0);
9405
        VIR_FREE(xml);
9406

9407 9408 9409 9410
        if (pool != NULL) {
            vshPrint(ctl, _("Pool %s created\n"), name);
            virStoragePoolFree(pool);
        } else {
9411
            vshError(ctl, _("Failed to create pool %s"), name);
E
Eric Blake 已提交
9412
            return false;
9413 9414
        }
    }
E
Eric Blake 已提交
9415
    return true;
9416 9417
}

9418

9419
/*
9420
 * "pool-define" command
9421
 */
9422
static const vshCmdInfo info_pool_define[] = {
9423 9424
    {"help", N_("define (but don't start) a pool from an XML file")},
    {"desc", N_("Define a pool.")},
9425 9426 9427
    {NULL, NULL}
};

9428
static const vshCmdOptDef opts_pool_define[] = {
9429
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML pool description")},
9430 9431 9432
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9433
static bool
9434
cmdPoolDefine(vshControl *ctl, const vshCmd *cmd)
9435
{
9436
    virStoragePoolPtr pool;
9437
    const char *from = NULL;
E
Eric Blake 已提交
9438
    bool ret = true;
9439
    char *buffer;
9440

9441
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9442
        return false;
9443

9444
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
9445
        return false;
9446

9447
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
9448
        return false;
9449

9450
    pool = virStoragePoolDefineXML(ctl->conn, buffer, 0);
9451
    VIR_FREE(buffer);
9452 9453 9454 9455

    if (pool != NULL) {
        vshPrint(ctl, _("Pool %s defined from %s\n"),
                 virStoragePoolGetName(pool), from);
L
Laine Stump 已提交
9456
        virStoragePoolFree(pool);
9457
    } else {
9458
        vshError(ctl, _("Failed to define pool from %s"), from);
E
Eric Blake 已提交
9459
        ret = false;
9460 9461
    }
    return ret;
9462 9463
}

9464

9465 9466 9467
/*
 * "pool-define-as" command
 */
9468
static const vshCmdInfo info_pool_define_as[] = {
9469 9470
    {"help", N_("define a pool from a set of args")},
    {"desc", N_("Define a pool.")},
9471 9472 9473
    {NULL, NULL}
};

E
Eric Blake 已提交
9474
static bool
9475
cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd)
9476 9477
{
    virStoragePoolPtr pool;
9478 9479
    const char *name;
    char *xml;
9480
    int printXML = vshCommandOptBool(cmd, "print-xml");
9481

9482
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9483
        return false;
9484

9485
    if (!buildPoolXML(cmd, &name, &xml))
E
Eric Blake 已提交
9486
        return false;
9487

9488
    if (printXML) {
9489
        vshPrint(ctl, "%s", xml);
9490
        VIR_FREE(xml);
9491
    } else {
9492
        pool = virStoragePoolDefineXML(ctl->conn, xml, 0);
9493
        VIR_FREE(xml);
9494

9495 9496 9497 9498
        if (pool != NULL) {
            vshPrint(ctl, _("Pool %s defined\n"), name);
            virStoragePoolFree(pool);
        } else {
9499
            vshError(ctl, _("Failed to define pool %s"), name);
E
Eric Blake 已提交
9500
            return false;
9501 9502
        }
    }
E
Eric Blake 已提交
9503
    return true;
9504 9505 9506
}


9507
/*
9508
 * "pool-build" command
9509
 */
9510
static const vshCmdInfo info_pool_build[] = {
9511 9512
    {"help", N_("build a pool")},
    {"desc", N_("Build a given pool.")},
9513 9514 9515
    {NULL, NULL}
};

9516
static const vshCmdOptDef opts_pool_build[] = {
9517
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
9518 9519
    {"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")},
9520 9521 9522
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9523
static bool
9524
cmdPoolBuild(vshControl *ctl, const vshCmd *cmd)
9525
{
9526
    virStoragePoolPtr pool;
E
Eric Blake 已提交
9527
    bool ret = true;
9528
    const char *name;
9529
    unsigned int flags = 0;
9530

9531
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9532
        return false;
9533

9534
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
9535
        return false;
9536

9537 9538 9539 9540 9541 9542 9543 9544 9545
    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) {
9546
        vshPrint(ctl, _("Pool %s built\n"), name);
9547
    } else {
9548
        vshError(ctl, _("Failed to build pool %s"), name);
E
Eric Blake 已提交
9549
        ret = false;
9550 9551
    }

9552 9553
    virStoragePoolFree(pool);

9554 9555 9556
    return ret;
}

9557
/*
9558
 * "pool-destroy" command
9559
 */
9560
static const vshCmdInfo info_pool_destroy[] = {
9561 9562 9563
    {"help", N_("destroy (stop) a pool")},
    {"desc",
     N_("Forcefully stop a given pool. Raw data in the pool is untouched")},
9564 9565 9566
    {NULL, NULL}
};

9567
static const vshCmdOptDef opts_pool_destroy[] = {
9568
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
9569 9570 9571
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9572
static bool
9573
cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd)
9574
{
9575
    virStoragePoolPtr pool;
E
Eric Blake 已提交
9576
    bool ret = true;
9577
    const char *name;
9578

9579
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9580
        return false;
9581

9582
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
9583
        return false;
9584

9585 9586 9587
    if (virStoragePoolDestroy(pool) == 0) {
        vshPrint(ctl, _("Pool %s destroyed\n"), name);
    } else {
9588
        vshError(ctl, _("Failed to destroy pool %s"), name);
E
Eric Blake 已提交
9589
        ret = false;
9590 9591
    }

9592
    virStoragePoolFree(pool);
9593 9594 9595
    return ret;
}

9596

9597
/*
9598 9599
 * "pool-delete" command
 */
9600
static const vshCmdInfo info_pool_delete[] = {
9601 9602
    {"help", N_("delete a pool")},
    {"desc", N_("Delete a given pool.")},
9603 9604 9605
    {NULL, NULL}
};

9606
static const vshCmdOptDef opts_pool_delete[] = {
9607
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
9608 9609 9610
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9611
static bool
9612
cmdPoolDelete(vshControl *ctl, const vshCmd *cmd)
9613 9614
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
9615
    bool ret = true;
9616
    const char *name;
9617

9618
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9619
        return false;
9620 9621

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
9622
        return false;
9623 9624

    if (virStoragePoolDelete(pool, 0) == 0) {
D
Daniel Veillard 已提交
9625
        vshPrint(ctl, _("Pool %s deleted\n"), name);
9626
    } else {
9627
        vshError(ctl, _("Failed to delete pool %s"), name);
E
Eric Blake 已提交
9628
        ret = false;
9629 9630
    }

L
Laine Stump 已提交
9631
    virStoragePoolFree(pool);
9632 9633 9634 9635 9636 9637 9638
    return ret;
}


/*
 * "pool-refresh" command
 */
9639
static const vshCmdInfo info_pool_refresh[] = {
9640 9641
    {"help", N_("refresh a pool")},
    {"desc", N_("Refresh a given pool.")},
9642 9643 9644
    {NULL, NULL}
};

9645
static const vshCmdOptDef opts_pool_refresh[] = {
9646
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
9647 9648 9649
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9650
static bool
9651
cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd)
9652 9653
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
9654
    bool ret = true;
9655
    const char *name;
9656

9657
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9658
        return false;
9659 9660

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
9661
        return false;
9662 9663 9664 9665

    if (virStoragePoolRefresh(pool, 0) == 0) {
        vshPrint(ctl, _("Pool %s refreshed\n"), name);
    } else {
9666
        vshError(ctl, _("Failed to refresh pool %s"), name);
E
Eric Blake 已提交
9667
        ret = false;
9668 9669 9670 9671 9672 9673 9674 9675 9676 9677
    }
    virStoragePoolFree(pool);

    return ret;
}


/*
 * "pool-dumpxml" command
 */
9678
static const vshCmdInfo info_pool_dumpxml[] = {
9679 9680
    {"help", N_("pool information in XML")},
    {"desc", N_("Output the pool information as an XML dump to stdout.")},
9681 9682 9683
    {NULL, NULL}
};

9684
static const vshCmdOptDef opts_pool_dumpxml[] = {
9685
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
9686 9687 9688
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9689
static bool
9690
cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
9691 9692
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
9693
    bool ret = true;
9694 9695
    char *dump;

9696
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9697
        return false;
9698 9699

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
9700
        return false;
9701 9702 9703

    dump = virStoragePoolGetXMLDesc(pool, 0);
    if (dump != NULL) {
9704
        vshPrint(ctl, "%s", dump);
9705
        VIR_FREE(dump);
9706
    } else {
E
Eric Blake 已提交
9707
        ret = false;
9708 9709 9710 9711 9712 9713 9714 9715 9716 9717
    }

    virStoragePoolFree(pool);
    return ret;
}


/*
 * "pool-list" command
 */
9718
static const vshCmdInfo info_pool_list[] = {
9719 9720
    {"help", N_("list pools")},
    {"desc", N_("Returns list of pools.")},
9721 9722 9723
    {NULL, NULL}
};

9724
static const vshCmdOptDef opts_pool_list[] = {
9725 9726
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")},
9727
    {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")},
9728 9729 9730
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9731
static bool
9732
cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
9733
{
9734 9735
    virStoragePoolInfo info;
    char **poolNames = NULL;
W
Wen Congyang 已提交
9736 9737
    int i, ret;
    bool functionReturn;
9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753
    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 */
9754
    int all = vshCommandOptBool(cmd, "all");
9755 9756
    int details = vshCommandOptBool(cmd, "details");
    int inactive = vshCommandOptBool(cmd, "inactive");
9757 9758 9759
    int active = !inactive || all ? 1 : 0;
    inactive |= all;

9760
    /* Check the connection to libvirtd daemon is still working */
9761
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9762
        return false;
9763

9764
    /* Retrieve the number of active storage pools */
9765
    if (active) {
9766 9767
        numActivePools = virConnectNumOfStoragePools(ctl->conn);
        if (numActivePools < 0) {
9768
            vshError(ctl, "%s", _("Failed to list active pools"));
E
Eric Blake 已提交
9769
            return false;
9770 9771
        }
    }
9772 9773

    /* Retrieve the number of inactive storage pools */
9774
    if (inactive) {
9775 9776
        numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn);
        if (numInactivePools < 0) {
9777
            vshError(ctl, "%s", _("Failed to list inactive pools"));
E
Eric Blake 已提交
9778
            return false;
9779
        }
9780
    }
9781

9782 9783
    /* Determine the total number of pools to list */
    numAllPools = numActivePools + numInactivePools;
9784

9785 9786 9787 9788 9789 9790 9791 9792 9793 9794 9795 9796
    /* 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 已提交
9797
            return false;
9798 9799 9800
        }
    }

9801 9802 9803 9804 9805 9806 9807 9808
    /* 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 已提交
9809
            return false;
9810 9811
        }
    }
9812

9813 9814 9815 9816 9817 9818 9819 9820 9821 9822
    /* 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]);
9823
        if (!pool) {
9824
            VIR_FREE(poolNames[i]);
9825 9826 9827
            continue;
        }

9828
        /* Retrieve the autostart status of the pool */
9829
        if (virStoragePoolGetAutostart(pool, &autostart) < 0)
9830
            poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart"));
9831
        else
9832 9833 9834 9835 9836 9837
            poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ?
                                                    _("yes") : _("no"));

        /* Retrieve the persistence status of the pool */
        if (details) {
            persistent = virStoragePoolIsPersistent(pool);
9838 9839
            vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n",
                     persistent);
9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850
            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;
        }
9851

9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963
        /* 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 */
9964 9965 9966
        virStoragePoolFree(pool);
    }

9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984
    /* 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);
9985 9986
        }

9987
        /* Cleanup and return */
E
Eric Blake 已提交
9988
        functionReturn = true;
9989 9990
        goto cleanup;
    }
9991

9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029
    /* 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. */
10030
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest name string = %lu chars\n",
10031
             (unsigned long) nameStrLength);
10032
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest state string = %lu chars\n",
10033
             (unsigned long) stateStrLength);
10034
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest autostart string = %lu chars\n",
10035
             (unsigned long) autostartStrLength);
10036
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest persistent string = %lu chars\n",
10037
             (unsigned long) persistStrLength);
10038
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest capacity string = %lu chars\n",
10039
             (unsigned long) capStrLength);
10040
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest allocation string = %lu chars\n",
10041
             (unsigned long) allocStrLength);
10042
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest available string = %lu chars\n",
10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 10054 10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085
             (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 已提交
10086
    functionReturn = true;
10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100
    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 已提交
10101
    functionReturn = false;
10102

10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113 10114
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]);
10115
    }
10116 10117 10118 10119 10120 10121 10122

    /* Cleanup the memory for the initial arrays*/
    VIR_FREE(poolInfoTexts);
    VIR_FREE(poolNames);

    /* Return the desired value */
    return functionReturn;
10123 10124
}

10125 10126 10127 10128
/*
 * "find-storage-pool-sources-as" command
 */
static const vshCmdInfo info_find_storage_pool_sources_as[] = {
10129 10130
    {"help", N_("find potential storage pool sources")},
    {"desc", N_("Returns XML <sources> document.")},
10131 10132 10133 10134 10135
    {NULL, NULL}
};

static const vshCmdOptDef opts_find_storage_pool_sources_as[] = {
    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
10136 10137 10138
     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")},
10139
    {"initiator", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional initiator IQN to use for query")},
10140 10141 10142
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10143
static bool
10144 10145
cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
10146
    const char *type = NULL, *host = NULL;
10147 10148
    char *srcSpec = NULL;
    char *srcList;
10149
    const char *initiator = NULL;
10150

10151 10152
    if (vshCommandOptString(cmd, "type", &type) <= 0 ||
        vshCommandOptString(cmd, "host", &host) < 0 ||
10153 10154
        vshCommandOptString(cmd, "initiator", &initiator) < 0) {
        vshError(ctl,"%s", _("missing argument"));
E
Eric Blake 已提交
10155
        return false;
10156
    }
10157

10158
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10159
        return false;
10160 10161

    if (host) {
10162
        const char *port = NULL;
10163
        virBuffer buf = VIR_BUFFER_INITIALIZER;
10164 10165 10166 10167

        if (vshCommandOptString(cmd, "port", &port) < 0) {
            vshError(ctl, "%s", _("missing argument"));
            virBufferFreeAndReset(&buf);
E
Eric Blake 已提交
10168
            return false;
10169
        }
10170
        virBufferAddLit(&buf, "<source>\n");
10171
        virBufferAsprintf(&buf, "  <host name='%s'", host);
10172
        if (port)
10173
            virBufferAsprintf(&buf, " port='%s'", port);
10174
        virBufferAddLit(&buf, "/>\n");
10175 10176
        if (initiator) {
            virBufferAddLit(&buf, "  <initiator>\n");
10177
            virBufferAsprintf(&buf, "    <iqn name='%s'/>\n", initiator);
10178 10179
            virBufferAddLit(&buf, "  </initiator>\n");
        }
10180 10181 10182
        virBufferAddLit(&buf, "</source>\n");
        if (virBufferError(&buf)) {
            vshError(ctl, "%s", _("Out of memory"));
E
Eric Blake 已提交
10183
            return false;
10184
        }
10185
        srcSpec = virBufferContentAndReset(&buf);
10186 10187 10188
    }

    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
10189
    VIR_FREE(srcSpec);
10190
    if (srcList == NULL) {
10191
        vshError(ctl, _("Failed to find any %s pool sources"), type);
E
Eric Blake 已提交
10192
        return false;
10193 10194
    }
    vshPrint(ctl, "%s", srcList);
10195
    VIR_FREE(srcList);
10196

E
Eric Blake 已提交
10197
    return true;
10198 10199 10200 10201 10202 10203 10204
}


/*
 * "find-storage-pool-sources" command
 */
static const vshCmdInfo info_find_storage_pool_sources[] = {
10205 10206
    {"help", N_("discover potential storage pool sources")},
    {"desc", N_("Returns XML <sources> document.")},
10207 10208 10209 10210 10211
    {NULL, NULL}
};

static const vshCmdOptDef opts_find_storage_pool_sources[] = {
    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
10212
     N_("type of storage pool sources to discover")},
10213
    {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE,
10214
     N_("optional file of source xml to query for pools")},
10215 10216 10217
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10218
static bool
10219 10220
cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
10221
    const char *type = NULL, *srcSpecFile = NULL;
10222
    char *srcSpec = NULL, *srcList;
10223

10224
    if (vshCommandOptString(cmd, "type", &type) <= 0)
E
Eric Blake 已提交
10225
        return false;
10226

10227 10228
    if (vshCommandOptString(cmd, "srcSpec", &srcSpecFile) < 0) {
        vshError(ctl, "%s", _("missing option"));
E
Eric Blake 已提交
10229
        return false;
10230
    }
10231

10232
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10233
        return false;
10234 10235

    if (srcSpecFile && virFileReadAll(srcSpecFile, VIRSH_MAX_XML_FILE, &srcSpec) < 0)
E
Eric Blake 已提交
10236
        return false;
10237 10238

    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
10239
    VIR_FREE(srcSpec);
10240
    if (srcList == NULL) {
10241
        vshError(ctl, _("Failed to find any %s pool sources"), type);
E
Eric Blake 已提交
10242
        return false;
10243 10244
    }
    vshPrint(ctl, "%s", srcList);
10245
    VIR_FREE(srcList);
10246

E
Eric Blake 已提交
10247
    return true;
10248 10249 10250
}


10251 10252 10253
/*
 * "pool-info" command
 */
10254
static const vshCmdInfo info_pool_info[] = {
10255 10256
    {"help", N_("storage pool information")},
    {"desc", N_("Returns basic information about the storage pool.")},
10257 10258 10259
    {NULL, NULL}
};

10260
static const vshCmdOptDef opts_pool_info[] = {
10261
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10262 10263 10264
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10265
static bool
10266
cmdPoolInfo(vshControl *ctl, const vshCmd *cmd)
10267 10268 10269
{
    virStoragePoolInfo info;
    virStoragePoolPtr pool;
10270 10271
    int autostart = 0;
    int persistent = 0;
E
Eric Blake 已提交
10272
    bool ret = true;
10273 10274
    char uuid[VIR_UUID_STRING_BUFLEN];

10275
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10276
        return false;
10277 10278

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
10279
        return false;
10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305

    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;
10306 10307 10308 10309
        case VIR_STORAGE_POOL_INACCESSIBLE:
            vshPrint(ctl, "%-15s %s\n", _("State:"),
                     _("inaccessible"));
            break;
10310 10311
        }

10312 10313
        /* Check and display whether the pool is persistent or not */
        persistent = virStoragePoolIsPersistent(pool);
10314 10315
        vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n",
                 persistent);
10316 10317 10318 10319 10320 10321 10322
        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);
10323 10324
        vshDebug(ctl, VSH_ERR_DEBUG, "Pool autostart flag value: %d\n",
                 autostart);
10325 10326 10327 10328 10329
        if (autostart < 0)
            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
        else
            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));

10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341
        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 已提交
10342
        ret = false;
10343 10344 10345 10346 10347 10348 10349 10350 10351 10352
    }

    virStoragePoolFree(pool);
    return ret;
}


/*
 * "pool-name" command
 */
10353
static const vshCmdInfo info_pool_name[] = {
10354
    {"help", N_("convert a pool UUID to pool name")},
10355
    {"desc", ""},
10356 10357 10358
    {NULL, NULL}
};

10359
static const vshCmdOptDef opts_pool_name[] = {
10360
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool uuid")},
10361 10362 10363
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10364
static bool
10365
cmdPoolName(vshControl *ctl, const vshCmd *cmd)
10366 10367 10368
{
    virStoragePoolPtr pool;

10369
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10370
        return false;
10371
    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
10372
                                           VSH_BYUUID)))
E
Eric Blake 已提交
10373
        return false;
10374 10375 10376

    vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
    virStoragePoolFree(pool);
E
Eric Blake 已提交
10377
    return true;
10378 10379 10380 10381 10382 10383
}


/*
 * "pool-start" command
 */
10384
static const vshCmdInfo info_pool_start[] = {
10385 10386
    {"help", N_("start a (previously defined) inactive pool")},
    {"desc", N_("Start a pool.")},
10387 10388 10389
    {NULL, NULL}
};

10390
static const vshCmdOptDef opts_pool_start[] = {
10391
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive pool")},
10392 10393 10394
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10395
static bool
10396
cmdPoolStart(vshControl *ctl, const vshCmd *cmd)
10397 10398
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
10399
    bool ret = true;
10400

10401
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10402
        return false;
10403

10404
    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME)))
E
Eric Blake 已提交
10405
         return false;
10406 10407 10408 10409 10410

    if (virStoragePoolCreate(pool, 0) == 0) {
        vshPrint(ctl, _("Pool %s started\n"),
                 virStoragePoolGetName(pool));
    } else {
10411
        vshError(ctl, _("Failed to start pool %s"), virStoragePoolGetName(pool));
E
Eric Blake 已提交
10412
        ret = false;
10413
    }
L
Laine Stump 已提交
10414 10415

    virStoragePoolFree(pool);
10416 10417 10418 10419
    return ret;
}


10420 10421 10422
/*
 * "vol-create-as" command
 */
10423
static const vshCmdInfo info_vol_create_as[] = {
10424 10425
    {"help", N_("create a volume from a set of args")},
    {"desc", N_("Create a vol.")},
10426 10427 10428
    {NULL, NULL}
};

10429
static const vshCmdOptDef opts_vol_create_as[] = {
10430 10431 10432 10433 10434
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ, N_("size of the vol with optional k,M,G,T suffix")},
    {"allocation", VSH_OT_STRING, 0, N_("initial allocation size with optional k,M,G,T suffix")},
    {"format", VSH_OT_STRING, 0, N_("file format type raw,bochs,qcow,qcow2,vmdk")},
10435 10436
    {"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")},
10437 10438 10439 10440 10441 10442 10443 10444 10445 10446
    {NULL, 0, 0, NULL}
};

static int cmdVolSize(const char *data, unsigned long long *val)
{
    char *end;
    if (virStrToLong_ull(data, &end, 10, val) < 0)
        return -1;

    if (end && *end) {
D
Daniel Veillard 已提交
10447
        /* Deliberate fallthrough cases here :-) */
10448 10449 10450
        switch (*end) {
        case 'T':
            *val *= 1024;
10451
            /* fallthrough */
10452 10453
        case 'G':
            *val *= 1024;
10454
            /* fallthrough */
10455 10456
        case 'M':
            *val *= 1024;
10457
            /* fallthrough */
10458 10459 10460 10461 10462 10463 10464 10465 10466 10467 10468 10469 10470
        case 'k':
            *val *= 1024;
            break;
        default:
            return -1;
        }
        end++;
        if (*end)
            return -1;
    }
    return 0;
}

E
Eric Blake 已提交
10471
static bool
10472
cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
10473 10474 10475
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
10476
    char *xml;
10477 10478
    const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL;
    const char *snapshotStrVol = NULL, *snapshotStrFormat = NULL;
10479
    unsigned long long capacity, allocation = 0;
10480
    virBuffer buf = VIR_BUFFER_INITIALIZER;
10481

10482
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10483
        return false;
10484 10485 10486

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
                                     VSH_BYNAME)))
E
Eric Blake 已提交
10487
        return false;
10488

10489
    if (vshCommandOptString(cmd, "name", &name) <= 0)
10490 10491
        goto cleanup;

10492
    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
10493 10494
        goto cleanup;
    if (cmdVolSize(capacityStr, &capacity) < 0)
10495
        vshError(ctl, _("Malformed size %s"), capacityStr);
10496

10497 10498
    if ((vshCommandOptString(cmd, "allocation", &allocationStr) > 0) &&
        (cmdVolSize(allocationStr, &allocation) < 0))
10499
        vshError(ctl, _("Malformed size %s"), allocationStr);
10500

10501 10502 10503 10504 10505 10506 10507
    if (vshCommandOptString(cmd, "format", &format) < 0 ||
        vshCommandOptString(cmd, "backing-vol", &snapshotStrVol) < 0 ||
        vshCommandOptString(cmd, "backing-vol-format",
                            &snapshotStrFormat) < 0) {
        vshError(ctl, "%s", _("missing argument"));
    }

10508

10509
    virBufferAddLit(&buf, "<volume>\n");
10510 10511
    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
    virBufferAsprintf(&buf, "  <capacity>%llu</capacity>\n", capacity);
10512
    if (allocationStr)
10513
        virBufferAsprintf(&buf, "  <allocation>%llu</allocation>\n", allocation);
10514 10515

    if (format) {
10516
        virBufferAddLit(&buf, "  <target>\n");
10517
        virBufferAsprintf(&buf, "    <format type='%s'/>\n",format);
10518
        virBufferAddLit(&buf, "  </target>\n");
10519
    }
10520

10521 10522 10523 10524
    /* Convert the snapshot parameters into backingStore XML */
    if (snapshotStrVol) {
        /* Lookup snapshot backing volume.  Try the backing-vol
         *  parameter as a name */
10525 10526
        vshDebug(ctl, VSH_ERR_DEBUG,
                 "%s: Look up backing store volume '%s' as name\n",
10527 10528 10529
                 cmd->def->name, snapshotStrVol);
        virStorageVolPtr snapVol = virStorageVolLookupByName(pool, snapshotStrVol);
        if (snapVol)
10530 10531
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as name\n",
10532 10533 10534 10535 10536
                         cmd->def->name, snapshotStrVol);

        if (snapVol == NULL) {
            /* Snapshot backing volume not found by name.  Try the
             *  backing-vol parameter as a key */
10537 10538
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: Look up backing store volume '%s' as key\n",
10539 10540 10541
                     cmd->def->name, snapshotStrVol);
            snapVol = virStorageVolLookupByKey(ctl->conn, snapshotStrVol);
            if (snapVol)
10542 10543
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as key\n",
10544 10545 10546 10547 10548
                         cmd->def->name, snapshotStrVol);
        }
        if (snapVol == NULL) {
            /* Snapshot backing volume not found by key.  Try the
             *  backing-vol parameter as a path */
10549 10550
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: Look up backing store volume '%s' as path\n",
10551 10552 10553
                     cmd->def->name, snapshotStrVol);
            snapVol = virStorageVolLookupByPath(ctl->conn, snapshotStrVol);
            if (snapVol)
10554 10555
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as path\n",
10556 10557 10558 10559
                         cmd->def->name, snapshotStrVol);
        }
        if (snapVol == NULL) {
            vshError(ctl, _("failed to get vol '%s'"), snapshotStrVol);
10560
            goto cleanup;
10561 10562 10563 10564 10565
        }

        char *snapshotStrVolPath;
        if ((snapshotStrVolPath = virStorageVolGetPath(snapVol)) == NULL) {
            virStorageVolFree(snapVol);
10566
            goto cleanup;
10567 10568 10569 10570
        }

        /* Create XML for the backing store */
        virBufferAddLit(&buf, "  <backingStore>\n");
10571
        virBufferAsprintf(&buf, "    <path>%s</path>\n",snapshotStrVolPath);
10572
        if (snapshotStrFormat)
10573
            virBufferAsprintf(&buf, "    <format type='%s'/>\n",snapshotStrFormat);
10574 10575 10576 10577 10578 10579 10580 10581
        virBufferAddLit(&buf, "  </backingStore>\n");

        /* Cleanup snapshot allocations */
        VIR_FREE(snapshotStrVolPath);
        virStorageVolFree(snapVol);
    }

    virBufferAddLit(&buf, "</volume>\n");
10582

10583 10584
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
10585
        goto cleanup;
10586 10587 10588
    }
    xml = virBufferContentAndReset(&buf);
    vol = virStorageVolCreateXML(pool, xml, 0);
10589
    VIR_FREE(xml);
10590 10591 10592 10593 10594
    virStoragePoolFree(pool);

    if (vol != NULL) {
        vshPrint(ctl, _("Vol %s created\n"), name);
        virStorageVolFree(vol);
E
Eric Blake 已提交
10595
        return true;
10596
    } else {
10597
        vshError(ctl, _("Failed to create vol %s"), name);
E
Eric Blake 已提交
10598
        return false;
10599 10600 10601
    }

 cleanup:
10602
    virBufferFreeAndReset(&buf);
10603
    virStoragePoolFree(pool);
E
Eric Blake 已提交
10604
    return false;
10605 10606 10607
}


10608 10609 10610
/*
 * "pool-undefine" command
 */
10611
static const vshCmdInfo info_pool_undefine[] = {
10612 10613
    {"help", N_("undefine an inactive pool")},
    {"desc", N_("Undefine the configuration for an inactive pool.")},
10614 10615 10616
    {NULL, NULL}
};

10617
static const vshCmdOptDef opts_pool_undefine[] = {
10618
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
10619 10620 10621
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10622
static bool
10623
cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd)
10624 10625
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
10626
    bool ret = true;
10627
    const char *name;
10628

10629
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10630
        return false;
10631 10632

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
10633
        return false;
10634 10635 10636 10637

    if (virStoragePoolUndefine(pool) == 0) {
        vshPrint(ctl, _("Pool %s has been undefined\n"), name);
    } else {
10638
        vshError(ctl, _("Failed to undefine pool %s"), name);
E
Eric Blake 已提交
10639
        ret = false;
10640 10641
    }

L
Laine Stump 已提交
10642
    virStoragePoolFree(pool);
10643 10644 10645 10646 10647 10648 10649
    return ret;
}


/*
 * "pool-uuid" command
 */
10650
static const vshCmdInfo info_pool_uuid[] = {
10651
    {"help", N_("convert a pool name to pool UUID")},
10652
    {"desc", ""},
10653 10654 10655
    {NULL, NULL}
};

10656
static const vshCmdOptDef opts_pool_uuid[] = {
10657
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
10658 10659 10660
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10661
static bool
10662
cmdPoolUuid(vshControl *ctl, const vshCmd *cmd)
10663 10664 10665 10666
{
    virStoragePoolPtr pool;
    char uuid[VIR_UUID_STRING_BUFLEN];

10667
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10668
        return false;
10669 10670

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
10671
                                           VSH_BYNAME)))
E
Eric Blake 已提交
10672
        return false;
10673 10674 10675 10676

    if (virStoragePoolGetUUIDString(pool, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
10677
        vshError(ctl, "%s", _("failed to get pool UUID"));
10678

L
Laine Stump 已提交
10679
    virStoragePoolFree(pool);
E
Eric Blake 已提交
10680
    return true;
10681 10682 10683 10684 10685 10686
}


/*
 * "vol-create" command
 */
10687
static const vshCmdInfo info_vol_create[] = {
10688 10689
    {"help", N_("create a vol from an XML file")},
    {"desc", N_("Create a vol.")},
10690 10691 10692
    {NULL, NULL}
};

10693
static const vshCmdOptDef opts_vol_create[] = {
10694 10695
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
10696 10697 10698
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10699
static bool
10700
cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
10701 10702 10703
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
10704
    const char *from = NULL;
E
Eric Blake 已提交
10705
    bool ret = true;
10706 10707
    char *buffer;

10708
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10709
        return false;
10710 10711

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
10712
                                           VSH_BYNAME)))
E
Eric Blake 已提交
10713
        return false;
10714

10715
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
10716
        virStoragePoolFree(pool);
E
Eric Blake 已提交
10717
        return false;
10718 10719 10720
    }

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
10721
        virshReportError(ctl);
10722
        virStoragePoolFree(pool);
E
Eric Blake 已提交
10723
        return false;
10724 10725 10726
    }

    vol = virStorageVolCreateXML(pool, buffer, 0);
10727
    VIR_FREE(buffer);
10728 10729 10730 10731 10732 10733 10734
    virStoragePoolFree(pool);

    if (vol != NULL) {
        vshPrint(ctl, _("Vol %s created from %s\n"),
                 virStorageVolGetName(vol), from);
        virStorageVolFree(vol);
    } else {
10735
        vshError(ctl, _("Failed to create vol from %s"), from);
E
Eric Blake 已提交
10736
        ret = false;
10737 10738 10739 10740
    }
    return ret;
}

10741 10742 10743 10744
/*
 * "vol-create-from" command
 */
static const vshCmdInfo info_vol_create_from[] = {
10745 10746
    {"help", N_("create a vol, using another volume as input")},
    {"desc", N_("Create a vol from an existing volume.")},
10747 10748 10749 10750
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_create_from[] = {
10751 10752 10753
    {"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 已提交
10754
    {"inputpool", VSH_OT_STRING, 0, N_("pool name or uuid of the input volume's pool")},
10755 10756 10757
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10758
static bool
10759 10760 10761 10762
cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool = NULL;
    virStorageVolPtr newvol = NULL, inputvol = NULL;
10763
    const char *from = NULL;
E
Eric Blake 已提交
10764
    bool ret = false;
10765 10766
    char *buffer = NULL;

10767
    if (!vshConnectionUsability(ctl, ctl->conn))
10768 10769 10770 10771 10772
        goto cleanup;

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME)))
        goto cleanup;

10773
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
10774 10775 10776 10777 10778 10779 10780
        goto cleanup;
    }

    if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL)))
        goto cleanup;

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
10781
        virshReportError(ctl);
10782 10783 10784 10785 10786 10787 10788 10789 10790
        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 {
10791
        vshError(ctl, _("Failed to create vol from %s"), from);
10792 10793 10794
        goto cleanup;
    }

E
Eric Blake 已提交
10795
    ret = true;
10796
cleanup:
10797
    VIR_FREE(buffer);
10798 10799 10800 10801
    if (pool)
        virStoragePoolFree(pool);
    if (inputvol)
        virStorageVolFree(inputvol);
L
Laine Stump 已提交
10802 10803
    if (newvol)
        virStorageVolFree(newvol);
10804 10805 10806 10807
    return ret;
}

static xmlChar *
10808 10809
makeCloneXML(const char *origxml, const char *newname)
{
10810

10811 10812 10813
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
10814 10815 10816
    xmlChar *newxml = NULL;
    int size;

10817
    doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
10818 10819 10820 10821 10822 10823 10824 10825 10826 10827 10828 10829 10830 10831 10832 10833 10834 10835 10836 10837 10838 10839
    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[] = {
10840 10841
    {"help", N_("clone a volume.")},
    {"desc", N_("Clone an existing volume.")},
10842 10843 10844 10845
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_clone[] = {
10846 10847
    {"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 已提交
10848
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
10849 10850 10851
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10852
static bool
10853 10854 10855 10856
cmdVolClone(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr origpool = NULL;
    virStorageVolPtr origvol = NULL, newvol = NULL;
10857
    const char *name = NULL;
10858
    char *origxml = NULL;
10859
    xmlChar *newxml = NULL;
E
Eric Blake 已提交
10860
    bool ret = false;
10861

10862
    if (!vshConnectionUsability(ctl, ctl->conn))
10863 10864 10865 10866 10867 10868 10869
        goto cleanup;

    if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
        goto cleanup;

    origpool = virStoragePoolLookupByVolume(origvol);
    if (!origpool) {
10870
        vshError(ctl, "%s", _("failed to get parent pool"));
10871 10872 10873
        goto cleanup;
    }

10874
    if (vshCommandOptString(cmd, "newname", &name) <= 0)
10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892
        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 {
10893
        vshError(ctl, _("Failed to clone vol from %s"),
10894 10895 10896 10897
                 virStorageVolGetName(origvol));
        goto cleanup;
    }

E
Eric Blake 已提交
10898
    ret = true;
10899 10900

cleanup:
10901
    VIR_FREE(origxml);
10902 10903 10904
    xmlFree(newxml);
    if (origvol)
        virStorageVolFree(origvol);
L
Laine Stump 已提交
10905 10906
    if (newvol)
        virStorageVolFree(newvol);
10907 10908 10909 10910 10911
    if (origpool)
        virStoragePoolFree(origpool);
    return ret;
}

10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924

/*
 * "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 已提交
10925
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939
    {"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 已提交
10940
static bool
10941 10942 10943 10944
cmdVolUpload (vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    virStorageVolPtr vol = NULL;
E
Eric Blake 已提交
10945
    bool ret = false;
10946 10947 10948 10949 10950 10951 10952 10953 10954 10955
    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 已提交
10956
        return false;
10957 10958 10959 10960
    }

    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
10961
        return false;
10962 10963 10964
    }

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
10965
        return false;
10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 10992 10993 10994 10995 10996 10997 10998 10999
    }

    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 已提交
11000
    ret = true;
11001 11002 11003 11004 11005 11006 11007 11008 11009 11010 11011 11012 11013 11014 11015 11016 11017 11018 11019 11020 11021 11022 11023 11024

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 已提交
11025
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11026 11027 11028 11029 11030
    {"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 已提交
11031
static bool
11032 11033 11034 11035
cmdVolDownload (vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    virStorageVolPtr vol = NULL;
E
Eric Blake 已提交
11036
    bool ret = false;
11037 11038 11039 11040
    int fd = -1;
    virStreamPtr st = NULL;
    const char *name = NULL;
    unsigned long long offset = 0, length = 0;
11041
    bool created = false;
11042 11043

    if (!vshConnectionUsability(ctl, ctl->conn))
11044
        return false;
11045 11046 11047

    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
11048
        return false;
11049 11050 11051 11052
    }

    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
11053
        return false;
11054 11055 11056
    }

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name)))
E
Eric Blake 已提交
11057
        return false;
11058 11059 11060 11061 11062 11063 11064 11065 11066 11067 11068 11069

    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;
        }
11070 11071
    } else {
        created = true;
11072 11073 11074 11075 11076 11077 11078 11079
    }

    st = virStreamNew(ctl->conn, 0);
    if (virStorageVolDownload(vol, st, offset, length, 0) < 0) {
        vshError(ctl, _("cannot download from volume %s"), name);
        goto cleanup;
    }

11080
    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
11081 11082 11083 11084 11085 11086 11087 11088 11089 11090 11091 11092 11093 11094 11095
        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 已提交
11096
    ret = true;
11097 11098

cleanup:
11099
    VIR_FORCE_CLOSE(fd);
11100
    if (!ret && created)
11101 11102 11103 11104 11105 11106 11107 11108 11109
        unlink(file);
    if (vol)
        virStorageVolFree(vol);
    if (st)
        virStreamFree(st);
    return ret;
}


11110 11111 11112
/*
 * "vol-delete" command
 */
11113
static const vshCmdInfo info_vol_delete[] = {
11114 11115
    {"help", N_("delete a vol")},
    {"desc", N_("Delete a given vol.")},
11116 11117 11118
    {NULL, NULL}
};

11119
static const vshCmdOptDef opts_vol_delete[] = {
11120
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
11121
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11122 11123 11124
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11125
static bool
11126
cmdVolDelete(vshControl *ctl, const vshCmd *cmd)
11127 11128
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
11129
    bool ret = true;
11130
    const char *name;
11131

11132
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11133
        return false;
11134 11135

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
11136
        return false;
11137 11138 11139
    }

    if (virStorageVolDelete(vol, 0) == 0) {
D
Daniel Veillard 已提交
11140
        vshPrint(ctl, _("Vol %s deleted\n"), name);
11141
    } else {
11142
        vshError(ctl, _("Failed to delete vol %s"), name);
E
Eric Blake 已提交
11143
        ret = false;
11144 11145
    }

L
Laine Stump 已提交
11146
    virStorageVolFree(vol);
11147 11148 11149 11150
    return ret;
}


D
David Allan 已提交
11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161
/*
 * "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 已提交
11162
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
D
David Allan 已提交
11163 11164 11165
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11166
static bool
D
David Allan 已提交
11167 11168 11169
cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
11170
    bool ret = true;
11171
    const char *name;
D
David Allan 已提交
11172

11173
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11174
        return false;
D
David Allan 已提交
11175 11176

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
11177
        return false;
D
David Allan 已提交
11178 11179 11180 11181 11182 11183
    }

    if (virStorageVolWipe(vol, 0) == 0) {
        vshPrint(ctl, _("Vol %s wiped\n"), name);
    } else {
        vshError(ctl, _("Failed to wipe vol %s"), name);
E
Eric Blake 已提交
11184
        ret = false;
D
David Allan 已提交
11185 11186 11187 11188 11189 11190 11191
    }

    virStorageVolFree(vol);
    return ret;
}


11192 11193 11194
/*
 * "vol-info" command
 */
11195
static const vshCmdInfo info_vol_info[] = {
11196 11197
    {"help", N_("storage vol information")},
    {"desc", N_("Returns basic information about the storage vol.")},
11198 11199 11200
    {NULL, NULL}
};

11201
static const vshCmdOptDef opts_vol_info[] = {
11202
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
11203
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11204 11205 11206
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11207
static bool
11208
cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
11209 11210 11211
{
    virStorageVolInfo info;
    virStorageVolPtr vol;
E
Eric Blake 已提交
11212
    bool ret = true;
11213

11214
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11215
        return false;
11216 11217

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
11218
        return false;
11219 11220 11221 11222 11223 11224

    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol));

    if (virStorageVolGetInfo(vol, &info) == 0) {
        double val;
        const char *unit;
R
Ryota Ozaki 已提交
11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240
        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"));
        }
11241 11242 11243 11244 11245 11246 11247

        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 已提交
11248
        ret = false;
11249 11250 11251 11252 11253 11254 11255 11256 11257 11258
    }

    virStorageVolFree(vol);
    return ret;
}


/*
 * "vol-dumpxml" command
 */
11259
static const vshCmdInfo info_vol_dumpxml[] = {
11260 11261
    {"help", N_("vol information in XML")},
    {"desc", N_("Output the vol information as an XML dump to stdout.")},
11262 11263 11264
    {NULL, NULL}
};

11265
static const vshCmdOptDef opts_vol_dumpxml[] = {
11266
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
11267
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11268 11269 11270
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11271
static bool
11272
cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
11273 11274
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
11275
    bool ret = true;
11276 11277
    char *dump;

11278
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11279
        return false;
11280 11281

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
11282
        return false;
11283 11284 11285

    dump = virStorageVolGetXMLDesc(vol, 0);
    if (dump != NULL) {
11286
        vshPrint(ctl, "%s", dump);
11287
        VIR_FREE(dump);
11288
    } else {
E
Eric Blake 已提交
11289
        ret = false;
11290 11291 11292 11293 11294 11295 11296 11297 11298 11299
    }

    virStorageVolFree(vol);
    return ret;
}


/*
 * "vol-list" command
 */
11300
static const vshCmdInfo info_vol_list[] = {
11301 11302
    {"help", N_("list vols")},
    {"desc", N_("Returns list of vols by pool.")},
11303 11304 11305
    {NULL, NULL}
};

11306
static const vshCmdOptDef opts_vol_list[] = {
11307
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
11308
    {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")},
11309 11310 11311
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11312
static bool
11313
cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
11314
{
11315
    virStorageVolInfo volumeInfo;
11316 11317
    virStoragePoolPtr pool;
    char **activeNames = NULL;
11318 11319 11320 11321 11322
    char *outputStr = NULL;
    const char *unit;
    double val;
    int details = vshCommandOptBool(cmd, "details");
    int numVolumes = 0, i;
W
Wen Congyang 已提交
11323 11324
    int ret;
    bool functionReturn;
11325 11326 11327 11328 11329 11330 11331 11332 11333 11334 11335
    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;
11336

11337
    /* Check the connection to libvirtd daemon is still working */
11338
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11339
        return false;
11340

11341
    /* Look up the pool information given to us by the user */
11342
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
11343
        return false;
11344

11345 11346
    /* Determine the number of volumes in the pool */
    numVolumes = virStoragePoolNumOfVolumes(pool);
11347

11348 11349 11350
    if (numVolumes < 0) {
        vshError(ctl, "%s", _("Failed to list storage volumes"));
        virStoragePoolFree(pool);
E
Eric Blake 已提交
11351
        return false;
11352 11353
    }

11354 11355 11356 11357 11358
    /* 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) {
11359
            vshError(ctl, "%s", _("Failed to list active vols"));
11360
            VIR_FREE(activeNames);
11361
            virStoragePoolFree(pool);
E
Eric Blake 已提交
11362
            return false;
11363 11364
        }

11365 11366 11367 11368 11369
        /* Sort the volume names */
        qsort(&activeNames[0], numVolumes, sizeof(*activeNames), namesorter);

        /* Set aside memory for volume information pointers */
        volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts));
11370 11371
    }

11372 11373 11374 11375 11376
    /* Collect the rest of the volume information for display */
    for (i = 0; i < numVolumes; i++) {
        /* Retrieve volume info */
        virStorageVolPtr vol = virStorageVolLookupByName(pool,
                                                         activeNames[i]);
11377

11378 11379 11380 11381
        /* 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"));
11382 11383
        }

11384 11385 11386 11387 11388 11389 11390 11391 11392
        /* 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 */
11393

11394
                /* Volume type */
11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 11407
                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"));
                }
11408 11409 11410 11411 11412 11413 11414 11415 11416 11417 11418 11419 11420 11421 11422 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 11436 11437 11438 11439 11440 11441 11442 11443 11444 11445 11446 11447 11448 11449 11450 11451 11452 11453 11454 11455 11456 11457

                /* 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;
        }
11458

11459
        /* Cleanup memory allocation */
11460
        virStorageVolFree(vol);
11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478
    }

    /* 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 已提交
11479
        functionReturn = true;
11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 11493 11494 11495 11496 11497 11498 11499 11500 11501 11502 11503 11504 11505 11506 11507 11508 11509 11510
        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 */
11511 11512 11513 11514 11515 11516 11517 11518 11519 11520
    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);
11521 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554

    /* 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 已提交
11555
    functionReturn = true;
11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569
    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 已提交
11570
    functionReturn = false;
11571 11572 11573 11574 11575 11576 11577 11578 11579 11580

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);
11581
        VIR_FREE(activeNames[i]);
11582
    }
11583 11584 11585 11586

    /* Cleanup remaining memory */
    VIR_FREE(outputStr);
    VIR_FREE(volInfoTexts);
11587
    VIR_FREE(activeNames);
11588
    virStoragePoolFree(pool);
11589 11590 11591

    /* Return the desired value */
    return functionReturn;
11592 11593 11594 11595 11596 11597
}


/*
 * "vol-name" command
 */
11598
static const vshCmdInfo info_vol_name[] = {
11599
    {"help", N_("returns the volume name for a given volume key or path")},
11600
    {"desc", ""},
11601 11602 11603
    {NULL, NULL}
};

11604
static const vshCmdOptDef opts_vol_name[] = {
11605
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
11606 11607 11608
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11609
static bool
11610
cmdVolName(vshControl *ctl, const vshCmd *cmd)
11611 11612 11613
{
    virStorageVolPtr vol;

11614
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11615
        return false;
11616

11617
    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
11618
                                   VSH_BYUUID)))
E
Eric Blake 已提交
11619
        return false;
11620 11621 11622

    vshPrint(ctl, "%s\n", virStorageVolGetName(vol));
    virStorageVolFree(vol);
E
Eric Blake 已提交
11623
    return true;
11624 11625 11626
}


J
Justin Clift 已提交
11627 11628 11629 11630 11631 11632 11633 11634 11635 11636
/*
 * "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[] = {
11637
    {"uuid", VSH_OT_BOOL, 0, N_("return the pool uuid rather than pool name")},
J
Justin Clift 已提交
11638 11639 11640 11641
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11642
static bool
J
Justin Clift 已提交
11643 11644 11645 11646
cmdVolPool(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
11647
    char uuid[VIR_UUID_STRING_BUFLEN];
J
Justin Clift 已提交
11648 11649

    /* Check the connection to libvirtd daemon is still working */
11650
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11651
        return false;
J
Justin Clift 已提交
11652 11653

    /* Use the supplied string to locate the volume */
11654
    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
J
Justin Clift 已提交
11655
                                   VSH_BYUUID))) {
E
Eric Blake 已提交
11656
        return false;
J
Justin Clift 已提交
11657 11658 11659 11660 11661 11662 11663
    }

    /* 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 已提交
11664
        return false;
J
Justin Clift 已提交
11665 11666
    }

11667 11668 11669 11670 11671 11672 11673 11674 11675
    /* 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 已提交
11676 11677 11678 11679

    /* Cleanup */
    virStorageVolFree(vol);
    virStoragePoolFree(pool);
E
Eric Blake 已提交
11680
    return true;
J
Justin Clift 已提交
11681 11682
}

11683 11684 11685 11686

/*
 * "vol-key" command
 */
11687
static const vshCmdInfo info_vol_key[] = {
11688
    {"help", N_("returns the volume key for a given volume name or path")},
11689
    {"desc", ""},
11690 11691 11692
    {NULL, NULL}
};

11693
static const vshCmdOptDef opts_vol_key[] = {
11694
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or path")},
E
Eric Blake 已提交
11695
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11696 11697 11698
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11699
static bool
11700
cmdVolKey(vshControl *ctl, const vshCmd *cmd)
11701 11702 11703
{
    virStorageVolPtr vol;

11704
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11705
        return false;
11706

11707
    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
11708
        return false;
11709 11710 11711

    vshPrint(ctl, "%s\n", virStorageVolGetKey(vol));
    virStorageVolFree(vol);
E
Eric Blake 已提交
11712
    return true;
11713 11714 11715 11716 11717 11718 11719
}



/*
 * "vol-path" command
 */
11720
static const vshCmdInfo info_vol_path[] = {
11721
    {"help", N_("returns the volume path for a given volume name or key")},
11722
    {"desc", ""},
11723 11724 11725
    {NULL, NULL}
};

11726
static const vshCmdOptDef opts_vol_path[] = {
11727
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or key")},
E
Eric Blake 已提交
11728
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
11729 11730 11731
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11732
static bool
11733
cmdVolPath(vshControl *ctl, const vshCmd *cmd)
11734 11735
{
    virStorageVolPtr vol;
11736
    char * StorageVolPath;
11737

11738
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11739
        return false;
11740

11741
    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) {
E
Eric Blake 已提交
11742
        return false;
11743
    }
11744

11745 11746 11747 11748 11749 11750 11751
    if ((StorageVolPath = virStorageVolGetPath(vol)) == NULL) {
        virStorageVolFree(vol);
        return false;
    }

    vshPrint(ctl, "%s\n", StorageVolPath);
    VIR_FREE(StorageVolPath);
11752
    virStorageVolFree(vol);
E
Eric Blake 已提交
11753
    return true;
11754 11755 11756
}


11757 11758 11759 11760
/*
 * "secret-define" command
 */
static const vshCmdInfo info_secret_define[] = {
11761 11762
    {"help", N_("define or modify a secret from an XML file")},
    {"desc", N_("Define or modify a secret.")},
11763 11764
    {NULL, NULL}
};
11765

11766
static const vshCmdOptDef opts_secret_define[] = {
11767
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
11768 11769
    {NULL, 0, 0, NULL}
};
11770

E
Eric Blake 已提交
11771
static bool
11772 11773
cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
{
11774
    const char *from = NULL;
11775
    char *buffer;
11776
    virSecretPtr res;
11777
    char uuid[VIR_UUID_STRING_BUFLEN];
11778

11779
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11780
        return false;
11781

11782
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
11783
        return false;
11784 11785

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
11786
        return false;
11787 11788

    res = virSecretDefineXML(ctl->conn, buffer, 0);
11789
    VIR_FREE(buffer);
11790 11791

    if (res == NULL) {
11792
        vshError(ctl, _("Failed to set attributes from %s"), from);
E
Eric Blake 已提交
11793
        return false;
11794
    }
11795
    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
11796
        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
11797
        virSecretFree(res);
E
Eric Blake 已提交
11798
        return false;
11799 11800 11801
    }
    vshPrint(ctl, _("Secret %s created\n"), uuid);
    virSecretFree(res);
E
Eric Blake 已提交
11802
    return true;
11803 11804 11805 11806 11807 11808
}

/*
 * "secret-dumpxml" command
 */
static const vshCmdInfo info_secret_dumpxml[] = {
11809 11810
    {"help", N_("secret attributes in XML")},
    {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
11811 11812 11813 11814
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_dumpxml[] = {
11815
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
11816 11817 11818
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11819
static bool
11820 11821 11822
cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
E
Eric Blake 已提交
11823
    bool ret = false;
11824 11825
    char *xml;

11826
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11827
        return false;
11828 11829 11830

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
11831
        return false;
11832 11833 11834 11835

    xml = virSecretGetXMLDesc(secret, 0);
    if (xml == NULL)
        goto cleanup;
11836
    vshPrint(ctl, "%s", xml);
11837
    VIR_FREE(xml);
E
Eric Blake 已提交
11838
    ret = true;
11839 11840 11841 11842 11843 11844 11845 11846 11847 11848

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-set-value" command
 */
static const vshCmdInfo info_secret_set_value[] = {
11849 11850
    {"help", N_("set a secret value")},
    {"desc", N_("Set a secret value.")},
11851 11852 11853 11854
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_set_value[] = {
11855 11856
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
    {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
11857 11858 11859
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11860
static bool
11861 11862 11863 11864
cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    size_t value_size;
11865
    const char *base64 = NULL;
11866
    char *value;
E
Eric Blake 已提交
11867 11868
    int res;
    bool ret = false;
11869

11870
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11871
        return false;
11872 11873 11874

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
11875
        return false;
11876

11877
    if (vshCommandOptString(cmd, "base64", &base64) <= 0)
11878 11879 11880
        goto cleanup;

    if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
J
Jim Meyering 已提交
11881
        vshError(ctl, "%s", _("Invalid base64 data"));
11882 11883 11884
        goto cleanup;
    }
    if (value == NULL) {
11885
        vshError(ctl, "%s", _("Failed to allocate memory"));
E
Eric Blake 已提交
11886
        return false;
11887 11888 11889 11890
    }

    res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
    memset(value, 0, value_size);
11891
    VIR_FREE(value);
11892 11893

    if (res != 0) {
11894
        vshError(ctl, "%s", _("Failed to set secret value"));
11895 11896 11897
        goto cleanup;
    }
    vshPrint(ctl, "%s", _("Secret value set\n"));
E
Eric Blake 已提交
11898
    ret = true;
11899 11900 11901 11902 11903 11904 11905 11906 11907 11908

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-get-value" command
 */
static const vshCmdInfo info_secret_get_value[] = {
11909 11910
    {"help", N_("Output a secret value")},
    {"desc", N_("Output a secret value to stdout.")},
11911 11912 11913 11914
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_get_value[] = {
11915
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
11916 11917 11918
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11919
static bool
11920 11921 11922 11923 11924 11925
cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    char *base64;
    unsigned char *value;
    size_t value_size;
E
Eric Blake 已提交
11926
    bool ret = false;
11927

11928
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11929
        return false;
11930 11931 11932

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
11933
        return false;
11934 11935 11936 11937 11938 11939 11940

    value = virSecretGetValue(secret, &value_size, 0);
    if (value == NULL)
        goto cleanup;

    base64_encode_alloc((char *)value, value_size, &base64);
    memset(value, 0, value_size);
11941
    VIR_FREE(value);
11942 11943

    if (base64 == NULL) {
11944
        vshError(ctl, "%s", _("Failed to allocate memory"));
11945 11946
        goto cleanup;
    }
11947
    vshPrint(ctl, "%s", base64);
11948
    memset(base64, 0, strlen(base64));
11949
    VIR_FREE(base64);
E
Eric Blake 已提交
11950
    ret = true;
11951 11952 11953 11954 11955 11956 11957 11958 11959 11960

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-undefine" command
 */
static const vshCmdInfo info_secret_undefine[] = {
11961 11962
    {"help", N_("undefine a secret")},
    {"desc", N_("Undefine a secret.")},
11963 11964 11965 11966
    {NULL, NULL}
};

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

E
Eric Blake 已提交
11971
static bool
11972 11973 11974
cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
E
Eric Blake 已提交
11975
    bool ret = false;
11976
    const char *uuid;
11977

11978
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11979
        return false;
11980 11981 11982

    secret = vshCommandOptSecret(ctl, cmd, &uuid);
    if (secret == NULL)
E
Eric Blake 已提交
11983
        return false;
11984 11985

    if (virSecretUndefine(secret) < 0) {
11986
        vshError(ctl, _("Failed to delete secret %s"), uuid);
11987 11988 11989
        goto cleanup;
    }
    vshPrint(ctl, _("Secret %s deleted\n"), uuid);
E
Eric Blake 已提交
11990
    ret = true;
11991 11992 11993 11994 11995 11996 11997 11998 11999 12000

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-list" command
 */
static const vshCmdInfo info_secret_list[] = {
12001 12002
    {"help", N_("list secrets")},
    {"desc", N_("Returns a list of secrets")},
12003 12004 12005
    {NULL, NULL}
};

E
Eric Blake 已提交
12006
static bool
12007 12008 12009 12010 12011
cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    int maxuuids = 0, i;
    char **uuids = NULL;

12012
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12013
        return false;
12014 12015 12016

    maxuuids = virConnectNumOfSecrets(ctl->conn);
    if (maxuuids < 0) {
12017
        vshError(ctl, "%s", _("Failed to list secrets"));
E
Eric Blake 已提交
12018
        return false;
12019 12020 12021 12022 12023
    }
    uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);

    maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
    if (maxuuids < 0) {
12024
        vshError(ctl, "%s", _("Failed to list secrets"));
12025
        VIR_FREE(uuids);
E
Eric Blake 已提交
12026
        return false;
12027 12028 12029 12030
    }

    qsort(uuids, maxuuids, sizeof(char *), namesorter);

12031 12032
    vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
    vshPrintExtra(ctl, "-----------------------------------------------------------\n");
12033 12034

    for (i = 0; i < maxuuids; i++) {
12035 12036 12037 12038
        virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]);
        const char *usageType = NULL;

        if (!sec) {
12039
            VIR_FREE(uuids[i]);
12040 12041 12042 12043 12044 12045 12046 12047 12048 12049 12050 12051 12052 12053 12054 12055 12056 12057
            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);
12058
        VIR_FREE(uuids[i]);
12059
    }
12060
    VIR_FREE(uuids);
E
Eric Blake 已提交
12061
    return true;
12062
}
12063 12064 12065 12066 12067


/*
 * "version" command
 */
12068
static const vshCmdInfo info_version[] = {
12069 12070
    {"help", N_("show version")},
    {"desc", N_("Display the system version information.")},
12071 12072 12073
    {NULL, NULL}
};

12074 12075 12076 12077
static const vshCmdOptDef opts_version[] = {
    {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
    {NULL, 0, 0, NULL}
};
12078

E
Eric Blake 已提交
12079
static bool
12080
cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
12081 12082 12083 12084 12085 12086
{
    unsigned long hvVersion;
    const char *hvType;
    unsigned long libVersion;
    unsigned long includeVersion;
    unsigned long apiVersion;
12087
    unsigned long daemonVersion;
12088 12089 12090 12091 12092
    int ret;
    unsigned int major;
    unsigned int minor;
    unsigned int rel;

12093
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12094
        return false;
12095 12096 12097

    hvType = virConnectGetType(ctl->conn);
    if (hvType == NULL) {
12098
        vshError(ctl, "%s", _("failed to get hypervisor type"));
E
Eric Blake 已提交
12099
        return false;
12100 12101 12102 12103 12104 12105 12106 12107 12108 12109 12110 12111
    }

    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) {
12112
        vshError(ctl, "%s", _("failed to get the library version"));
E
Eric Blake 已提交
12113
        return false;
12114 12115 12116 12117 12118 12119 12120 12121 12122 12123 12124 12125 12126 12127 12128 12129 12130
    }
    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) {
12131
        vshError(ctl, "%s", _("failed to get the hypervisor version"));
E
Eric Blake 已提交
12132
        return false;
12133 12134 12135 12136 12137 12138 12139 12140 12141 12142 12143 12144 12145
    }
    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);
    }
12146 12147 12148 12149 12150 12151 12152 12153 12154 12155 12156 12157 12158 12159 12160

    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 已提交
12161
    return true;
12162 12163
}

12164 12165 12166 12167
/*
 * "nodedev-list" command
 */
static const vshCmdInfo info_node_list_devices[] = {
12168
    {"help", N_("enumerate devices on this host")},
12169
    {"desc", ""},
12170 12171 12172 12173
    {NULL, NULL}
};

static const vshCmdOptDef opts_node_list_devices[] = {
12174 12175
    {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
    {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
12176 12177 12178
    {NULL, 0, 0, NULL}
};

12179 12180 12181 12182 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 12193 12194 12195 12196 12197 12198 12199 12200
#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] = '-';
12201 12202
        indentBuf[indentIdx+2] = ' ';
        indentBuf[indentIdx+3] = '\0';
12203 12204 12205
    }

    /* Print this device */
12206
    vshPrint(ctl, "%s", indentBuf);
12207 12208 12209 12210 12211 12212 12213 12214 12215 12216 12217 12218 12219 12220 12221 12222 12223 12224 12225 12226 12227 12228 12229
    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) {
12230
        vshPrint(ctl, "%s", indentBuf);
12231
        vshPrint(ctl, " |\n");
12232 12233 12234 12235 12236 12237 12238 12239 12240 12241 12242 12243 12244 12245 12246 12247 12248 12249 12250 12251 12252 12253
    }

    /* 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) {
12254
        vshPrint(ctl, "%s", indentBuf);
12255 12256 12257 12258
        vshPrint(ctl, "\n");
    }
}

E
Eric Blake 已提交
12259
static bool
12260 12261
cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
12262
    const char *cap = NULL;
12263
    char **devices;
12264
    int num_devices, i;
12265
    int tree = vshCommandOptBool(cmd, "tree");
12266

12267
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12268
        return false;
12269

12270
    if (vshCommandOptString(cmd, "cap", &cap) <= 0)
12271 12272 12273 12274
        cap = NULL;

    num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
    if (num_devices < 0) {
12275
        vshError(ctl, "%s", _("Failed to count node devices"));
E
Eric Blake 已提交
12276
        return false;
12277
    } else if (num_devices == 0) {
E
Eric Blake 已提交
12278
        return true;
12279 12280 12281 12282 12283 12284
    }

    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
    num_devices =
        virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
    if (num_devices < 0) {
12285
        vshError(ctl, "%s", _("Failed to list node devices"));
12286
        VIR_FREE(devices);
E
Eric Blake 已提交
12287
        return false;
12288
    }
12289
    qsort(&devices[0], num_devices, sizeof(char*), namesorter);
12290 12291 12292 12293 12294 12295 12296
    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 已提交
12297
                parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
12298 12299 12300 12301 12302 12303 12304 12305 12306 12307 12308 12309 12310 12311 12312 12313 12314 12315 12316
            } 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++) {
12317 12318
            VIR_FREE(devices[i]);
            VIR_FREE(parents[i]);
12319
        }
12320
        VIR_FREE(parents);
12321 12322 12323
    } else {
        for (i = 0; i < num_devices; i++) {
            vshPrint(ctl, "%s\n", devices[i]);
12324
            VIR_FREE(devices[i]);
12325
        }
12326
    }
12327
    VIR_FREE(devices);
E
Eric Blake 已提交
12328
    return true;
12329 12330 12331 12332 12333 12334
}

/*
 * "nodedev-dumpxml" command
 */
static const vshCmdInfo info_node_device_dumpxml[] = {
12335 12336
    {"help", N_("node device details in XML")},
    {"desc", N_("Output the node device details as an XML dump to stdout.")},
12337 12338 12339 12340 12341
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_dumpxml[] = {
12342
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
12343 12344 12345
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12346
static bool
12347 12348
cmdNodeDeviceDumpXML (vshControl *ctl, const vshCmd *cmd)
{
12349
    const char *name = NULL;
12350
    virNodeDevicePtr device;
L
Laine Stump 已提交
12351
    char *xml;
12352

12353
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12354
        return false;
12355
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
12356
        return false;
12357
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
12358
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
12359
        return false;
12360 12361
    }

L
Laine Stump 已提交
12362 12363 12364
    xml = virNodeDeviceGetXMLDesc(device, 0);
    if (!xml) {
        virNodeDeviceFree(device);
E
Eric Blake 已提交
12365
        return false;
L
Laine Stump 已提交
12366 12367 12368
    }

    vshPrint(ctl, "%s\n", xml);
12369
    VIR_FREE(xml);
12370
    virNodeDeviceFree(device);
E
Eric Blake 已提交
12371
    return true;
12372 12373
}

12374 12375 12376 12377
/*
 * "nodedev-dettach" command
 */
static const vshCmdInfo info_node_device_dettach[] = {
12378 12379
    {"help", N_("dettach node device from its device driver")},
    {"desc", N_("Dettach node device from its device driver before assigning to a domain.")},
12380 12381 12382 12383 12384
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_dettach[] = {
12385
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
12386 12387 12388
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12389
static bool
12390 12391
cmdNodeDeviceDettach (vshControl *ctl, const vshCmd *cmd)
{
12392
    const char *name = NULL;
12393
    virNodeDevicePtr device;
E
Eric Blake 已提交
12394
    bool ret = true;
12395

12396
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12397
        return false;
12398
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
12399
        return false;
12400
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
12401
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
12402
        return false;
12403 12404 12405 12406 12407
    }

    if (virNodeDeviceDettach(device) == 0) {
        vshPrint(ctl, _("Device %s dettached\n"), name);
    } else {
12408
        vshError(ctl, _("Failed to dettach device %s"), name);
E
Eric Blake 已提交
12409
        ret = false;
12410 12411 12412 12413 12414 12415 12416 12417 12418
    }
    virNodeDeviceFree(device);
    return ret;
}

/*
 * "nodedev-reattach" command
 */
static const vshCmdInfo info_node_device_reattach[] = {
12419 12420
    {"help", N_("reattach node device to its device driver")},
    {"desc", N_("Reattach node device to its device driver once released by the domain.")},
12421 12422 12423 12424 12425
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_reattach[] = {
12426
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
12427 12428 12429
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12430
static bool
12431 12432
cmdNodeDeviceReAttach (vshControl *ctl, const vshCmd *cmd)
{
12433
    const char *name = NULL;
12434
    virNodeDevicePtr device;
E
Eric Blake 已提交
12435
    bool ret = true;
12436

12437
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12438
        return false;
12439
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
12440
        return false;
12441
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
12442
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
12443
        return false;
12444 12445 12446 12447 12448
    }

    if (virNodeDeviceReAttach(device) == 0) {
        vshPrint(ctl, _("Device %s re-attached\n"), name);
    } else {
12449
        vshError(ctl, _("Failed to re-attach device %s"), name);
E
Eric Blake 已提交
12450
        ret = false;
12451 12452 12453 12454 12455 12456 12457 12458 12459
    }
    virNodeDeviceFree(device);
    return ret;
}

/*
 * "nodedev-reset" command
 */
static const vshCmdInfo info_node_device_reset[] = {
12460 12461
    {"help", N_("reset node device")},
    {"desc", N_("Reset node device before or after assigning to a domain.")},
12462 12463 12464 12465 12466
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_reset[] = {
12467
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
12468 12469 12470
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12471
static bool
12472 12473
cmdNodeDeviceReset (vshControl *ctl, const vshCmd *cmd)
{
12474
    const char *name = NULL;
12475
    virNodeDevicePtr device;
E
Eric Blake 已提交
12476
    bool ret = true;
12477

12478
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12479
        return false;
12480
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
12481
        return false;
12482
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
12483
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
12484
        return false;
12485 12486 12487 12488 12489
    }

    if (virNodeDeviceReset(device) == 0) {
        vshPrint(ctl, _("Device %s reset\n"), name);
    } else {
12490
        vshError(ctl, _("Failed to reset device %s"), name);
E
Eric Blake 已提交
12491
        ret = false;
12492 12493 12494 12495 12496
    }
    virNodeDeviceFree(device);
    return ret;
}

12497
/*
E
Eric Blake 已提交
12498
 * "hostname" command
12499
 */
12500
static const vshCmdInfo info_hostname[] = {
12501
    {"help", N_("print the hypervisor hostname")},
12502
    {"desc", ""},
12503 12504 12505
    {NULL, NULL}
};

E
Eric Blake 已提交
12506
static bool
12507
cmdHostname (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
12508 12509 12510
{
    char *hostname;

12511
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12512
        return false;
12513 12514 12515

    hostname = virConnectGetHostname (ctl->conn);
    if (hostname == NULL) {
12516
        vshError(ctl, "%s", _("failed to get hostname"));
E
Eric Blake 已提交
12517
        return false;
12518 12519 12520
    }

    vshPrint (ctl, "%s\n", hostname);
12521
    VIR_FREE(hostname);
12522

E
Eric Blake 已提交
12523
    return true;
12524 12525 12526 12527 12528
}

/*
 * "uri" command
 */
12529
static const vshCmdInfo info_uri[] = {
12530
    {"help", N_("print the hypervisor canonical URI")},
12531
    {"desc", ""},
12532 12533 12534
    {NULL, NULL}
};

E
Eric Blake 已提交
12535
static bool
12536
cmdURI (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
12537 12538 12539
{
    char *uri;

12540
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12541
        return false;
12542 12543 12544

    uri = virConnectGetURI (ctl->conn);
    if (uri == NULL) {
12545
        vshError(ctl, "%s", _("failed to get URI"));
E
Eric Blake 已提交
12546
        return false;
12547 12548 12549
    }

    vshPrint (ctl, "%s\n", uri);
12550
    VIR_FREE(uri);
12551

E
Eric Blake 已提交
12552
    return true;
12553 12554
}

E
Eric Blake 已提交
12555 12556 12557 12558 12559 12560 12561 12562 12563 12564
/*
 * "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 已提交
12565
static bool
E
Eric Blake 已提交
12566 12567 12568 12569 12570
cmdSysinfo (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *sysinfo;

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12571
        return false;
E
Eric Blake 已提交
12572 12573 12574 12575

    sysinfo = virConnectGetSysinfo (ctl->conn, 0);
    if (sysinfo == NULL) {
        vshError(ctl, "%s", _("failed to get sysinfo"));
E
Eric Blake 已提交
12576
        return false;
E
Eric Blake 已提交
12577 12578 12579 12580 12581
    }

    vshPrint (ctl, "%s", sysinfo);
    VIR_FREE(sysinfo);

E
Eric Blake 已提交
12582
    return true;
E
Eric Blake 已提交
12583 12584
}

12585 12586 12587
/*
 * "vncdisplay" command
 */
12588
static const vshCmdInfo info_vncdisplay[] = {
12589 12590
    {"help", N_("vnc display")},
    {"desc", N_("Output the IP address and port number for the VNC display.")},
12591 12592 12593
    {NULL, NULL}
};

12594
static const vshCmdOptDef opts_vncdisplay[] = {
12595
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
12596 12597 12598
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12599
static bool
12600
cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
12601 12602 12603 12604 12605
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virDomainPtr dom;
E
Eric Blake 已提交
12606
    bool ret = false;
12607 12608 12609
    int port = 0;
    char *doc;

12610
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12611
        return false;
12612

J
Jim Meyering 已提交
12613
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
12614
        return false;
12615 12616 12617 12618 12619

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

12620
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
12621
    VIR_FREE(doc);
12622 12623 12624 12625 12626 12627 12628 12629 12630 12631 12632 12633 12634 12635 12636
    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) ||
12637
        STREQ((const char*)obj->stringval, "0.0.0.0")) {
12638 12639 12640 12641 12642 12643
        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 已提交
12644
    ret = true;
12645 12646 12647 12648

 cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
12649
    xmlFreeDoc(xml);
12650 12651 12652 12653 12654 12655 12656
    virDomainFree(dom);
    return ret;
}

/*
 * "ttyconsole" command
 */
12657
static const vshCmdInfo info_ttyconsole[] = {
12658 12659
    {"help", N_("tty console")},
    {"desc", N_("Output the device for the TTY console.")},
12660 12661 12662
    {NULL, NULL}
};

12663
static const vshCmdOptDef opts_ttyconsole[] = {
12664
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
12665 12666 12667
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12668
static bool
12669
cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
12670 12671 12672 12673 12674
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virDomainPtr dom;
E
Eric Blake 已提交
12675
    bool ret = false;
12676 12677
    char *doc;

12678
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12679
        return false;
12680

J
Jim Meyering 已提交
12681
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
12682
        return false;
12683 12684 12685 12686 12687

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

12688
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
12689
    VIR_FREE(doc);
12690 12691 12692 12693 12694 12695 12696 12697 12698
    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 已提交
12699
    ret = true;
12700 12701 12702 12703

 cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
12704
    xmlFreeDoc(xml);
12705 12706 12707 12708 12709 12710
    virDomainFree(dom);
    return ret;
}

/*
 * "attach-device" command
12711
 */
12712
static const vshCmdInfo info_attach_device[] = {
12713 12714
    {"help", N_("attach device from an XML file")},
    {"desc", N_("Attach device from an XML <file>.")},
12715 12716 12717
    {NULL, NULL}
};

12718
static const vshCmdOptDef opts_attach_device[] = {
12719 12720 12721
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist device attachment")},
12722 12723 12724
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12725
static bool
12726
cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
12727 12728
{
    virDomainPtr dom;
12729
    const char *from = NULL;
12730
    char *buffer;
W
Wen Congyang 已提交
12731
    int ret;
J
Jim Fehlig 已提交
12732
    unsigned int flags;
12733

12734
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12735
        return false;
12736

J
Jim Meyering 已提交
12737
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
12738
        return false;
12739

12740
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
12741
        virDomainFree(dom);
E
Eric Blake 已提交
12742
        return false;
12743 12744
    }

12745
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
12746
        virshReportError(ctl);
12747
        virDomainFree(dom);
E
Eric Blake 已提交
12748
        return false;
12749
    }
12750

J
Jim Fehlig 已提交
12751
    if (vshCommandOptBool(cmd, "persistent")) {
12752
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
12753
        if (virDomainIsActive(dom) == 1)
12754
           flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
12755 12756 12757 12758
        ret = virDomainAttachDeviceFlags(dom, buffer, flags);
    } else {
        ret = virDomainAttachDevice(dom, buffer);
    }
12759
    VIR_FREE(buffer);
12760 12761

    if (ret < 0) {
12762
        vshError(ctl, _("Failed to attach device from %s"), from);
12763
        virDomainFree(dom);
E
Eric Blake 已提交
12764
        return false;
12765
    } else {
J
Jim Meyering 已提交
12766
        vshPrint(ctl, "%s", _("Device attached successfully\n"));
12767 12768 12769
    }

    virDomainFree(dom);
E
Eric Blake 已提交
12770
    return true;
12771 12772
}

12773 12774 12775 12776 12777 12778 12779 12780 12781 12782 12783 12784 12785 12786 12787 12788
/**
 * 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.
 */
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 已提交
12789
    long n1_child_size, n2_child_size, n1_iter;
12790 12791 12792 12793 12794 12795 12796 12797 12798 12799 12800 12801 12802 12803 12804 12805 12806 12807 12808 12809 12810 12811 12812 12813 12814 12815 12816 12817
    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 已提交
12818 12819 12820 12821
    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)
12822 12823
        return false;

12824 12825 12826
    if (n1_child_size == 0 && n2_child_size == 0)
        return true;

12827 12828 12829 12830 12831 12832 12833 12834 12835 12836 12837 12838 12839 12840 12841 12842 12843 12844 12845 12846 12847 12848 12849 12850 12851 12852 12853 12854 12855 12856 12857 12858 12859 12860 12861 12862 12863 12864 12865 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 12892 12893 12894 12895 12896 12897 12898 12899 12900 12901 12902 12903 12904 12905 12906 12907 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 12922 12923 12924 12925 12926 12927 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 12944 12945 12946 12947 12948 12949 12950 12951 12952 12953 12954 12955 12956 12957 12958 12959 12960 12961 12962 12963 12964 12965 12966 12967 12968 12969 12970 12971 12972 12973 12974 12975 12976 12977 12978 12979 12980 12981 12982 12983 12984 12985 12986 12987 12988 12989 12990 12991 12992 12993 12994 12995 12996 12997 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016
    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.
 */
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;
}
13017 13018 13019 13020

/*
 * "detach-device" command
 */
13021
static const vshCmdInfo info_detach_device[] = {
13022 13023
    {"help", N_("detach device from an XML file")},
    {"desc", N_("Detach device from an XML <file>")},
13024 13025 13026
    {NULL, NULL}
};

13027
static const vshCmdOptDef opts_detach_device[] = {
13028 13029 13030
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist device detachment")},
13031 13032 13033
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13034
static bool
13035
cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
13036
{
13037
    virDomainPtr dom = NULL;
13038
    const char *from = NULL;
13039
    char *buffer = NULL, *new_buffer = NULL;
W
Wen Congyang 已提交
13040
    int ret;
13041
    bool funcRet = false;
J
Jim Fehlig 已提交
13042
    unsigned int flags;
13043

13044
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13045
        return false;
13046

J
Jim Meyering 已提交
13047
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
13048
        return false;
13049

13050 13051
    if (vshCommandOptString(cmd, "file", &from) <= 0)
        goto cleanup;
13052

13053
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
13054
        virshReportError(ctl);
13055 13056 13057 13058 13059 13060 13061 13062 13063 13064 13065 13066 13067 13068 13069
        goto cleanup;
    }

    ret = vshCompleteXMLFromDomain(ctl, dom, buffer, &new_buffer);
    if (ret < 0) {
        if (ret == -2) {
            vshError(ctl, _("no such device in %s"), virDomainGetName(dom));
        } else if (ret == -3) {
            vshError(ctl, "%s", _("given XML selects too many devices. "
                                  "Please, be more specific"));
        } else {
            /* vshCompleteXMLFromDomain() already printed error message,
             * so nothing to do here. */
        }
        goto cleanup;
13070
    }
13071

J
Jim Fehlig 已提交
13072
    if (vshCommandOptBool(cmd, "persistent")) {
13073
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
13074
        if (virDomainIsActive(dom) == 1)
13075
           flags |= VIR_DOMAIN_AFFECT_LIVE;
13076
        ret = virDomainDetachDeviceFlags(dom, new_buffer, flags);
J
Jim Fehlig 已提交
13077
    } else {
13078
        ret = virDomainDetachDevice(dom, new_buffer);
J
Jim Fehlig 已提交
13079
    }
13080 13081

    if (ret < 0) {
13082
        vshError(ctl, _("Failed to detach device from %s"), from);
13083
        goto cleanup;
13084 13085
    }

13086 13087 13088 13089 13090 13091
    vshPrint(ctl, "%s", _("Device detached successfully\n"));
    funcRet = true;

cleanup:
    VIR_FREE(new_buffer);
    VIR_FREE(buffer);
13092
    virDomainFree(dom);
13093
    return funcRet;
13094 13095
}

13096

13097 13098 13099 13100 13101 13102 13103 13104 13105 13106 13107 13108 13109
/*
 * "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")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist device update")},
13110
    {"force",  VSH_OT_BOOL, 0, N_("force device update")},
13111 13112 13113
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13114
static bool
13115 13116 13117
cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
13118
    const char *from = NULL;
13119
    char *buffer;
W
Wen Congyang 已提交
13120
    int ret;
13121 13122
    unsigned int flags;

13123
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13124
        return false;
13125 13126

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

13129
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
13130
        virDomainFree(dom);
E
Eric Blake 已提交
13131
        return false;
13132 13133 13134
    }

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
13135
        virshReportError(ctl);
13136
        virDomainFree(dom);
E
Eric Blake 已提交
13137
        return false;
13138 13139 13140
    }

    if (vshCommandOptBool(cmd, "persistent")) {
13141
        flags = VIR_DOMAIN_AFFECT_CONFIG;
13142
        if (virDomainIsActive(dom) == 1)
13143
           flags |= VIR_DOMAIN_AFFECT_LIVE;
13144
    } else {
13145
        flags = VIR_DOMAIN_AFFECT_LIVE;
13146
    }
13147 13148 13149 13150

    if (vshCommandOptBool(cmd, "force"))
        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;

13151 13152 13153 13154 13155 13156
    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 已提交
13157
        return false;
13158 13159 13160 13161 13162
    } else {
        vshPrint(ctl, "%s", _("Device updated successfully\n"));
    }

    virDomainFree(dom);
E
Eric Blake 已提交
13163
    return true;
13164 13165 13166
}


13167 13168 13169
/*
 * "attach-interface" command
 */
13170
static const vshCmdInfo info_attach_interface[] = {
13171 13172
    {"help", N_("attach network interface")},
    {"desc", N_("Attach new network interface.")},
13173 13174 13175
    {NULL, NULL}
};

13176
static const vshCmdOptDef opts_attach_interface[] = {
13177 13178 13179 13180 13181 13182
    {"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")},
13183
    {"model", VSH_OT_DATA, 0, N_("model type")},
13184
    {"persistent", VSH_OT_BOOL, 0, N_("persist interface attachment")},
13185 13186
    {"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")},
13187 13188 13189
    {NULL, 0, 0, NULL}
};

13190 13191 13192
/* 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. */
13193
static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate)
13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224
{
    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 已提交
13225
static bool
13226
cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
13227 13228
{
    virDomainPtr dom = NULL;
13229
    const char *mac = NULL, *target = NULL, *script = NULL,
13230 13231
                *type = NULL, *source = NULL, *model = NULL,
                *inboundStr = NULL, *outboundStr = NULL;
13232
    virNetDevBandwidthRate inbound, outbound;
E
Eric Blake 已提交
13233
    int typ;
W
Wen Congyang 已提交
13234 13235
    int ret;
    bool functionReturn = false;
J
Jim Fehlig 已提交
13236
    unsigned int flags;
13237 13238
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *xml;
13239

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

J
Jim Meyering 已提交
13243
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
13244 13245
        goto cleanup;

13246
    if (vshCommandOptString(cmd, "type", &type) <= 0)
13247 13248
        goto cleanup;

13249 13250 13251 13252
    if (vshCommandOptString(cmd, "source", &source) < 0 ||
        vshCommandOptString(cmd, "target", &target) < 0 ||
        vshCommandOptString(cmd, "mac", &mac) < 0 ||
        vshCommandOptString(cmd, "script", &script) < 0 ||
13253 13254 13255
        vshCommandOptString(cmd, "model", &model) < 0 ||
        vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
13256
        vshError(ctl, "missing argument");
13257
        goto cleanup;
13258
    }
13259 13260

    /* check interface type */
13261
    if (STREQ(type, "network")) {
13262
        typ = 1;
13263
    } else if (STREQ(type, "bridge")) {
13264 13265
        typ = 2;
    } else {
E
Eric Blake 已提交
13266 13267
        vshError(ctl, _("No support for %s in command 'attach-interface'"),
                 type);
13268 13269 13270
        goto cleanup;
    }

13271 13272 13273 13274 13275 13276 13277 13278 13279 13280 13281 13282 13283 13284 13285 13286 13287 13288 13289 13290 13291 13292 13293
    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;
        }
    }

13294
    /* Make XML of interface */
13295
    virBufferAsprintf(&buf, "<interface type='%s'>\n", type);
13296

13297
    if (typ == 1)
13298
        virBufferAsprintf(&buf, "  <source network='%s'/>\n", source);
13299
    else if (typ == 2)
13300
        virBufferAsprintf(&buf, "  <source bridge='%s'/>\n", source);
13301

13302
    if (target != NULL)
13303
        virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
13304
    if (mac != NULL)
13305
        virBufferAsprintf(&buf, "  <mac address='%s'/>\n", mac);
13306
    if (script != NULL)
13307
        virBufferAsprintf(&buf, "  <script path='%s'/>\n", script);
13308
    if (model != NULL)
13309
        virBufferAsprintf(&buf, "  <model type='%s'/>\n", model);
13310

13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331
    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");
    }

13332
    virBufferAddLit(&buf, "</interface>\n");
13333

13334 13335
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
W
Wen Congyang 已提交
13336
        goto cleanup;
13337 13338
    }

13339
    xml = virBufferContentAndReset(&buf);
13340

J
Jim Fehlig 已提交
13341
    if (vshCommandOptBool(cmd, "persistent")) {
13342
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
13343
        if (virDomainIsActive(dom) == 1)
13344
            flags |= VIR_DOMAIN_AFFECT_LIVE;
13345
        ret = virDomainAttachDeviceFlags(dom, xml, flags);
13346
    } else {
13347
        ret = virDomainAttachDevice(dom, xml);
13348
    }
13349

13350 13351
    VIR_FREE(xml);

J
Jim Fehlig 已提交
13352
    if (ret != 0) {
L
Laine Stump 已提交
13353
        vshError(ctl, "%s", _("Failed to attach interface"));
J
Jim Fehlig 已提交
13354 13355
    } else {
        vshPrint(ctl, "%s", _("Interface attached successfully\n"));
W
Wen Congyang 已提交
13356
        functionReturn = true;
J
Jim Fehlig 已提交
13357
    }
13358 13359 13360 13361

 cleanup:
    if (dom)
        virDomainFree(dom);
13362
    virBufferFreeAndReset(&buf);
W
Wen Congyang 已提交
13363
    return functionReturn;
13364 13365 13366 13367 13368
}

/*
 * "detach-interface" command
 */
13369
static const vshCmdInfo info_detach_interface[] = {
13370 13371
    {"help", N_("detach network interface")},
    {"desc", N_("Detach network interface.")},
13372 13373 13374
    {NULL, NULL}
};

13375
static const vshCmdOptDef opts_detach_interface[] = {
13376 13377 13378 13379
    {"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")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist interface detachment")},
13380 13381 13382
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13383
static bool
13384
cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
13385 13386 13387 13388 13389 13390 13391
{
    virDomainPtr dom = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj=NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr cur = NULL;
    xmlBufferPtr xml_buf = NULL;
13392
    const char *mac =NULL, *type = NULL;
13393
    char *doc;
13394
    char buf[64];
E
Eric Blake 已提交
13395
    int i = 0, diff_mac;
W
Wen Congyang 已提交
13396 13397
    int ret;
    int functionReturn = false;
J
Jim Fehlig 已提交
13398
    unsigned int flags;
13399

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

J
Jim Meyering 已提交
13403
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
13404 13405
        goto cleanup;

13406
    if (vshCommandOptString(cmd, "type", &type) <= 0)
13407 13408
        goto cleanup;

13409 13410
    if (vshCommandOptString(cmd, "mac", &mac) < 0) {
        vshError(ctl, "%s", _("missing option"));
13411
        goto cleanup;
13412
    }
13413 13414 13415 13416 13417

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

13418
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
13419
    VIR_FREE(doc);
13420
    if (!xml) {
13421
        vshError(ctl, "%s", _("Failed to get interface information"));
13422 13423 13424
        goto cleanup;
    }

E
Eric Blake 已提交
13425
    snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
13426 13427 13428
    obj = xmlXPathEval(BAD_CAST buf, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
13429
        vshError(ctl, _("No found interface whose type is %s"), type);
13430 13431 13432
        goto cleanup;
    }

13433 13434 13435 13436 13437 13438
    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;
    }

13439 13440 13441 13442 13443 13444 13445
    if (!mac)
        goto hit;

    /* search mac */
    for (; i < obj->nodesetval->nodeNr; i++) {
        cur = obj->nodesetval->nodeTab[i]->children;
        while (cur != NULL) {
13446 13447 13448 13449 13450
            if (cur->type == XML_ELEMENT_NODE &&
                xmlStrEqual(cur->name, BAD_CAST "mac")) {
                char *tmp_mac = virXMLPropString(cur, "address");
                diff_mac = virMacAddrCompare (tmp_mac, mac);
                VIR_FREE(tmp_mac);
13451 13452 13453 13454 13455 13456 13457
                if (!diff_mac) {
                    goto hit;
                }
            }
            cur = cur->next;
        }
    }
13458
    vshError(ctl, _("No found interface whose MAC address is %s"), mac);
13459 13460 13461 13462 13463
    goto cleanup;

 hit:
    xml_buf = xmlBufferCreate();
    if (!xml_buf) {
13464
        vshError(ctl, "%s", _("Failed to allocate memory"));
13465 13466 13467
        goto cleanup;
    }

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

J
Jim Fehlig 已提交
13473
    if (vshCommandOptBool(cmd, "persistent")) {
13474
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
13475
        if (virDomainIsActive(dom) == 1)
13476
            flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
13477 13478 13479 13480 13481 13482 13483 13484
        ret = virDomainDetachDeviceFlags(dom,
                                         (char *)xmlBufferContent(xml_buf),
                                         flags);
    } else {
        ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
    }

    if (ret != 0) {
L
Laine Stump 已提交
13485
        vshError(ctl, "%s", _("Failed to detach interface"));
J
Jim Fehlig 已提交
13486
    } else {
J
Jim Meyering 已提交
13487
        vshPrint(ctl, "%s", _("Interface detached successfully\n"));
W
Wen Congyang 已提交
13488
        functionReturn = true;
13489
    }
13490 13491 13492 13493

 cleanup:
    if (dom)
        virDomainFree(dom);
13494
    xmlXPathFreeObject(obj);
13495
    xmlXPathFreeContext(ctxt);
13496 13497
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
W
Wen Congyang 已提交
13498
    return functionReturn;
13499 13500 13501 13502 13503
}

/*
 * "attach-disk" command
 */
13504
static const vshCmdInfo info_attach_disk[] = {
13505 13506
    {"help", N_("attach disk device")},
    {"desc", N_("Attach new disk device.")},
13507 13508 13509
    {NULL, NULL}
};

13510
static const vshCmdOptDef opts_attach_disk[] = {
13511
    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
13512 13513
    {"source",  VSH_OT_DATA, VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK,
     N_("source of disk device")},
13514 13515 13516
    {"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")},
13517
    {"cache",     VSH_OT_STRING, 0, N_("cache mode of disk device")},
13518 13519 13520
    {"type",    VSH_OT_STRING, 0, N_("target device type")},
    {"mode",    VSH_OT_STRING, 0, N_("mode of device reading and writing")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist disk attachment")},
13521
    {"sourcetype", VSH_OT_STRING, 0, N_("type of source (block|file)")},
13522 13523 13524
    {"serial", VSH_OT_STRING, 0, N_("serial of disk device")},
    {"shareable", VSH_OT_BOOL, 0, N_("shareable between domains")},
    {"address", VSH_OT_STRING, 0, N_("address of disk device")},
13525 13526
    {"multifunction", VSH_OT_BOOL, 0,
     N_("use multifunction pci under specified address")},
13527 13528 13529
    {NULL, 0, 0, NULL}
};

13530 13531 13532 13533 13534 13535 13536 13537 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 13566 13567 13568 13569 13570 13571 13572 13573 13574 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 13653 13654 13655 13656 13657 13658 13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 13673 13674 13675 13676
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 已提交
13677
static bool
13678
cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
13679 13680
{
    virDomainPtr dom = NULL;
13681
    const char *source = NULL, *target = NULL, *driver = NULL,
13682 13683 13684
                *subdriver = NULL, *type = NULL, *mode = NULL,
                *cache = NULL, *serial = NULL, *straddr = NULL;
    struct DiskAddress diskAddr;
W
Wen Congyang 已提交
13685 13686
    bool isFile = false, functionReturn = false;
    int ret;
J
Jim Fehlig 已提交
13687
    unsigned int flags;
13688
    const char *stype = NULL;
13689 13690
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *xml;
13691

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

J
Jim Meyering 已提交
13695
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
13696 13697
        goto cleanup;

13698
    if (vshCommandOptString(cmd, "source", &source) <= 0)
13699
        goto cleanup;
13700 13701 13702 13703
    /* Allow empty string as a placeholder that implies no source, for
     * use in adding a cdrom drive with no disk.  */
    if (!*source)
        source = NULL;
13704

13705
    if (vshCommandOptString(cmd, "target", &target) <= 0)
13706 13707
        goto cleanup;

13708 13709 13710 13711
    if (vshCommandOptString(cmd, "driver", &driver) < 0 ||
        vshCommandOptString(cmd, "subdriver", &subdriver) < 0 ||
        vshCommandOptString(cmd, "type", &type) < 0 ||
        vshCommandOptString(cmd, "mode", &mode) < 0 ||
13712 13713 13714
        vshCommandOptString(cmd, "cache", &cache) < 0 ||
        vshCommandOptString(cmd, "serial", &serial) < 0 ||
        vshCommandOptString(cmd, "address", &straddr) < 0 ||
13715
        vshCommandOptString(cmd, "sourcetype", &stype) < 0) {
13716
        vshError(ctl, "%s", _("missing option"));
13717 13718
        goto cleanup;
    }
13719

13720 13721
    if (!stype) {
        if (driver && (STREQ(driver, "file") || STREQ(driver, "tap")))
E
Eric Blake 已提交
13722
            isFile = true;
13723
    } else if (STREQ(stype, "file")) {
E
Eric Blake 已提交
13724
        isFile = true;
13725 13726 13727
    } else if (STRNEQ(stype, "block")) {
        vshError(ctl, _("Unknown source type: '%s'"), stype);
        goto cleanup;
13728 13729 13730
    }

    if (mode) {
13731
        if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
E
Eric Blake 已提交
13732 13733
            vshError(ctl, _("No support for %s in command 'attach-disk'"),
                     mode);
13734 13735 13736 13737 13738
            goto cleanup;
        }
    }

    /* Make XML of disk */
13739
    virBufferAsprintf(&buf, "<disk type='%s'",
13740 13741
                      (isFile) ? "file" : "block");
    if (type)
13742
        virBufferAsprintf(&buf, " device='%s'", type);
13743 13744
    virBufferAddLit(&buf, ">\n");

13745
    if (driver || subdriver)
13746
        virBufferAsprintf(&buf, "  <driver");
13747 13748

    if (driver)
13749
        virBufferAsprintf(&buf, " name='%s'", driver);
13750
    if (subdriver)
13751
        virBufferAsprintf(&buf, " type='%s'", subdriver);
13752 13753
    if (cache)
        virBufferAsprintf(&buf, " cache='%s'", cache);
13754

13755
    if (driver || subdriver || cache)
13756
        virBufferAddLit(&buf, "/>\n");
13757

13758 13759 13760 13761
    if (source)
        virBufferAsprintf(&buf, "  <source %s='%s'/>\n",
                          (isFile) ? "file" : "dev",
                          source);
13762
    virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
13763
    if (mode)
13764
        virBufferAsprintf(&buf, "  <%s/>\n", mode);
13765

13766 13767 13768 13769 13770 13771 13772 13773 13774 13775 13776 13777 13778 13779 13780 13781
    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'"
13782
                                  " bus ='0x%02x' slot='0x%02x' function='0x%0x'",
13783 13784
                                  diskAddr.addr.pci.domain, diskAddr.addr.pci.bus,
                                  diskAddr.addr.pci.slot, diskAddr.addr.pci.function);
13785 13786 13787
                if (vshCommandOptBool(cmd, "multifunction"))
                    virBufferAddLit(&buf, " multifunction='on'");
                virBufferAddLit(&buf, "/>\n");
13788 13789 13790 13791 13792 13793 13794 13795 13796 13797 13798 13799 13800 13801 13802 13803 13804 13805 13806 13807 13808 13809 13810 13811 13812 13813 13814 13815 13816
            } 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;
            }
        }
    }

13817
    virBufferAddLit(&buf, "</disk>\n");
13818

13819 13820
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
13821
        return false;
13822 13823
    }

13824
    xml = virBufferContentAndReset(&buf);
13825

J
Jim Fehlig 已提交
13826
    if (vshCommandOptBool(cmd, "persistent")) {
13827
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
13828
        if (virDomainIsActive(dom) == 1)
13829
            flags |= VIR_DOMAIN_AFFECT_LIVE;
13830
        ret = virDomainAttachDeviceFlags(dom, xml, flags);
J
Jim Fehlig 已提交
13831
    } else {
13832
        ret = virDomainAttachDevice(dom, xml);
J
Jim Fehlig 已提交
13833
    }
13834

13835 13836
    VIR_FREE(xml);

J
Jim Fehlig 已提交
13837
    if (ret != 0) {
L
Laine Stump 已提交
13838
        vshError(ctl, "%s", _("Failed to attach disk"));
J
Jim Fehlig 已提交
13839 13840
    } else {
        vshPrint(ctl, "%s", _("Disk attached successfully\n"));
W
Wen Congyang 已提交
13841
        functionReturn = true;
J
Jim Fehlig 已提交
13842
    }
13843 13844 13845 13846

 cleanup:
    if (dom)
        virDomainFree(dom);
13847
    virBufferFreeAndReset(&buf);
W
Wen Congyang 已提交
13848
    return functionReturn;
13849 13850 13851 13852 13853
}

/*
 * "detach-disk" command
 */
13854
static const vshCmdInfo info_detach_disk[] = {
13855 13856
    {"help", N_("detach disk device")},
    {"desc", N_("Detach disk device.")},
13857 13858 13859
    {NULL, NULL}
};

13860
static const vshCmdOptDef opts_detach_disk[] = {
13861 13862 13863
    {"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")},
    {"persistent", VSH_OT_BOOL, 0, N_("persist disk detachment")},
13864 13865 13866
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13867
static bool
13868
cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
13869 13870 13871 13872 13873 13874 13875
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj=NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr cur = NULL;
    xmlBufferPtr xml_buf = NULL;
    virDomainPtr dom = NULL;
13876
    const char *target = NULL;
13877
    char *doc;
E
Eric Blake 已提交
13878
    int i = 0, diff_tgt;
W
Wen Congyang 已提交
13879 13880
    int ret;
    bool functionReturn = false;
J
Jim Fehlig 已提交
13881
    unsigned int flags;
13882

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

J
Jim Meyering 已提交
13886
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
13887 13888
        goto cleanup;

13889
    if (vshCommandOptString(cmd, "target", &target) <= 0)
13890 13891 13892 13893 13894 13895
        goto cleanup;

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

13896
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
13897
    VIR_FREE(doc);
13898
    if (!xml) {
13899
        vshError(ctl, "%s", _("Failed to get disk information"));
13900 13901 13902 13903 13904 13905
        goto cleanup;
    }

    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
13906
        vshError(ctl, "%s", _("Failed to get disk information"));
13907 13908 13909 13910 13911 13912 13913
        goto cleanup;
    }

    /* search target */
    for (; i < obj->nodesetval->nodeNr; i++) {
        cur = obj->nodesetval->nodeTab[i]->children;
        while (cur != NULL) {
13914 13915 13916 13917 13918
            if (cur->type == XML_ELEMENT_NODE &&
                xmlStrEqual(cur->name, BAD_CAST "target")) {
                char *tmp_tgt = virXMLPropString(cur, "dev");
                diff_tgt = STREQ(tmp_tgt, target);
                VIR_FREE(tmp_tgt);
13919 13920 13921 13922 13923 13924 13925
                if (diff_tgt) {
                    goto hit;
                }
            }
            cur = cur->next;
        }
    }
13926
    vshError(ctl, _("No found disk whose target is %s"), target);
13927 13928 13929 13930 13931
    goto cleanup;

 hit:
    xml_buf = xmlBufferCreate();
    if (!xml_buf) {
13932
        vshError(ctl, "%s", _("Failed to allocate memory"));
13933 13934 13935
        goto cleanup;
    }

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

J
Jim Fehlig 已提交
13941
    if (vshCommandOptBool(cmd, "persistent")) {
13942
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
13943
        if (virDomainIsActive(dom) == 1)
13944
            flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
13945 13946 13947 13948 13949 13950 13951 13952
        ret = virDomainDetachDeviceFlags(dom,
                                         (char *)xmlBufferContent(xml_buf),
                                         flags);
    } else {
        ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
    }

    if (ret != 0) {
L
Laine Stump 已提交
13953
        vshError(ctl, "%s", _("Failed to detach disk"));
J
Jim Fehlig 已提交
13954
    } else {
J
Jim Meyering 已提交
13955
        vshPrint(ctl, "%s", _("Disk detached successfully\n"));
W
Wen Congyang 已提交
13956
        functionReturn = true;
13957
    }
13958 13959

 cleanup:
13960
    xmlXPathFreeObject(obj);
13961
    xmlXPathFreeContext(ctxt);
13962 13963
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
13964 13965
    if (dom)
        virDomainFree(dom);
W
Wen Congyang 已提交
13966
    return functionReturn;
13967 13968
}

13969 13970 13971 13972
/*
 * "cpu-compare" command
 */
static const vshCmdInfo info_cpu_compare[] = {
13973 13974
    {"help", N_("compare host CPU with a CPU described by an XML file")},
    {"desc", N_("compare CPU with host CPU")},
13975 13976 13977 13978
    {NULL, NULL}
};

static const vshCmdOptDef opts_cpu_compare[] = {
13979
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML CPU description")},
13980 13981 13982
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13983
static bool
13984 13985
cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
{
13986
    const char *from = NULL;
13987
    bool ret = false;
13988 13989
    char *buffer;
    int result;
13990 13991 13992 13993 13994 13995
    const char *snippet;

    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlBufferPtr xml_buf = NULL;
    xmlNodePtr node;
13996

13997
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
13998
        return false;
13999

14000
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
14001
        return false;
14002

14003 14004 14005
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
        vshError(ctl, _("Failed to read file '%s' to compare"),
                 from);
E
Eric Blake 已提交
14006
        return false;
14007
    }
14008

14009 14010 14011 14012 14013 14014 14015 14016 14017 14018 14019 14020 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033
    /* 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);
14034 14035 14036 14037 14038

    switch (result) {
    case VIR_CPU_COMPARE_INCOMPATIBLE:
        vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"),
                 from);
14039
        goto cleanup;
14040 14041 14042 14043 14044 14045 14046 14047 14048 14049 14050 14051 14052 14053 14054
        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);
14055
        goto cleanup;
14056 14057
    }

14058 14059 14060 14061 14062 14063 14064 14065
    ret = true;

cleanup:
    VIR_FREE(buffer);
    xmlBufferFree(xml_buf);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);

14066 14067 14068
    return ret;
}

14069 14070 14071 14072
/*
 * "cpu-baseline" command
 */
static const vshCmdInfo info_cpu_baseline[] = {
14073 14074
    {"help", N_("compute baseline CPU")},
    {"desc", N_("Compute baseline CPU for a set of given CPUs.")},
14075 14076 14077 14078
    {NULL, NULL}
};

static const vshCmdOptDef opts_cpu_baseline[] = {
14079
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing XML CPU descriptions")},
14080 14081 14082
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
14083
static bool
14084 14085
cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
{
14086
    const char *from = NULL;
14087
    bool ret = false;
14088 14089 14090
    char *buffer;
    char *result = NULL;
    const char **list = NULL;
14091 14092 14093 14094
    int count = 0;

    xmlDocPtr xml = NULL;
    xmlNodePtr *node_list = NULL;
14095
    xmlXPathContextPtr ctxt = NULL;
14096 14097 14098
    xmlBufferPtr xml_buf = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    int i;
14099

14100
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
14101
        return false;
14102

14103
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
14104
        return false;
14105 14106

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

E
Eric Blake 已提交
14109
    /* add a separate container around the xml */
14110 14111
    virBufferStrcat(&buf, "<container>", buffer, "</container>", NULL);
    if (virBufferError(&buf))
14112 14113
        goto no_memory;

14114 14115 14116 14117 14118 14119 14120 14121 14122 14123 14124 14125 14126
    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);
14127 14128 14129
        goto cleanup;
    }

14130
    list = vshCalloc(ctl, count, sizeof(const char *));
14131

14132
    if (!(xml_buf = xmlBufferCreate()))
14133 14134
        goto no_memory;

14135 14136
    for (i = 0; i < count; i++) {
        xmlBufferEmpty(xml_buf);
14137

14138 14139 14140
        if (xmlNodeDump(xml_buf, xml,  node_list[i], 0, 0) < 0) {
            vshError(ctl, _("Failed to extract <cpu> element"));
            goto cleanup;
14141 14142
        }

14143
        list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
14144 14145 14146 14147
    }

    result = virConnectBaselineCPU(ctl->conn, list, count, 0);

14148
    if (result) {
14149
        vshPrint(ctl, "%s", result);
14150 14151
        ret = true;
    }
14152 14153 14154

cleanup:
    xmlXPathFreeContext(ctxt);
14155 14156
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
14157 14158
    VIR_FREE(result);
    if ((list != NULL) && (count > 0)) {
14159
        for (i = 0; i < count; i++)
14160 14161 14162 14163 14164 14165 14166 14167 14168
            VIR_FREE(list[i]);
    }
    VIR_FREE(list);
    VIR_FREE(buffer);

    return ret;

no_memory:
    vshError(ctl, "%s", _("Out of memory"));
E
Eric Blake 已提交
14169
    ret = false;
14170
    goto cleanup;
14171 14172
}

14173 14174 14175 14176 14177 14178 14179 14180
/* 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;

14181
    ret = vshMalloc(ctl, PATH_MAX);
14182 14183 14184

    tmpdir = getenv ("TMPDIR");
    if (!tmpdir) tmpdir = "/tmp";
14185 14186
    snprintf (ret, PATH_MAX, "%s/virshXXXXXX.xml", tmpdir);
    fd = mkstemps(ret, 4);
14187
    if (fd == -1) {
14188
        vshError(ctl, _("mkstemps: failed to create temporary file: %s"),
14189
                 strerror(errno));
14190
        VIR_FREE(ret);
14191 14192 14193 14194
        return NULL;
    }

    if (safewrite (fd, doc, strlen (doc)) == -1) {
14195 14196
        vshError(ctl, _("write: %s: failed to write to temporary file: %s"),
                 ret, strerror(errno));
S
Stefan Berger 已提交
14197
        VIR_FORCE_CLOSE(fd);
14198
        unlink (ret);
14199
        VIR_FREE(ret);
14200 14201
        return NULL;
    }
S
Stefan Berger 已提交
14202
    if (VIR_CLOSE(fd) < 0) {
14203 14204
        vshError(ctl, _("close: %s: failed to write or close temporary file: %s"),
                 ret, strerror(errno));
14205
        unlink (ret);
14206
        VIR_FREE(ret);
14207 14208 14209 14210 14211 14212 14213 14214 14215 14216 14217 14218 14219 14220 14221
        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 已提交
14222 14223 14224 14225
    virCommandPtr cmd;
    int ret = -1;
    int outfd = STDOUT_FILENO;
    int errfd = STDERR_FILENO;
14226

14227
    editor = getenv ("VISUAL");
E
Eric Blake 已提交
14228 14229 14230 14231
    if (!editor)
        editor = getenv ("EDITOR");
    if (!editor)
        editor = "vi"; /* could be cruel & default to ed(1) here */
14232

14233 14234 14235 14236 14237
    /* 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 已提交
14238 14239
     * is why sudo scrubs it by default).  Conversely, if the editor
     * is safe, we can run it directly rather than wasting a shell.
14240
     */
E
Eric Blake 已提交
14241 14242 14243 14244 14245 14246 14247 14248 14249 14250 14251 14252
    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);
14253 14254
    }

E
Eric Blake 已提交
14255 14256 14257 14258 14259 14260 14261
    virCommandSetInputFD(cmd, STDIN_FILENO);
    virCommandSetOutputFD(cmd, &outfd);
    virCommandSetErrorFD(cmd, &errfd);
    if (virCommandRunAsync(cmd, NULL) < 0 ||
        virCommandWait(cmd, NULL) < 0) {
        virshReportError(ctl);
        goto cleanup;
14262
    }
E
Eric Blake 已提交
14263
    ret = 0;
14264

E
Eric Blake 已提交
14265 14266 14267
cleanup:
    virCommandFree(cmd);
    return ret;
14268 14269 14270 14271 14272 14273 14274 14275
}

static char *
editReadBackFile (vshControl *ctl, const char *filename)
{
    char *ret;

    if (virFileReadAll (filename, VIRSH_MAX_XML_FILE, &ret) == -1) {
14276
        vshError(ctl,
14277
                 _("%s: failed to read temporary file: %s"),
14278
                 filename, strerror(errno));
14279 14280 14281 14282 14283
        return NULL;
    }
    return ret;
}

14284

P
Paolo Bonzini 已提交
14285 14286 14287 14288
/*
 * "cd" command
 */
static const vshCmdInfo info_cd[] = {
14289 14290
    {"help", N_("change the current directory")},
    {"desc", N_("Change the current directory.")},
P
Paolo Bonzini 已提交
14291 14292 14293 14294
    {NULL, NULL}
};

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

E
Eric Blake 已提交
14299
static bool
14300
cmdCd(vshControl *ctl, const vshCmd *cmd)
P
Paolo Bonzini 已提交
14301
{
14302
    const char *dir = NULL;
14303
    char *dir_malloced = NULL;
E
Eric Blake 已提交
14304
    bool ret = true;
P
Paolo Bonzini 已提交
14305 14306

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

14311
    if (vshCommandOptString(cmd, "dir", &dir) <= 0) {
P
Paolo Bonzini 已提交
14312
        uid_t uid = geteuid();
14313
        dir = dir_malloced = virGetUserDirectory(uid);
P
Paolo Bonzini 已提交
14314 14315 14316 14317
    }
    if (!dir)
        dir = "/";

P
Phil Petty 已提交
14318
    if (chdir(dir) == -1) {
14319
        vshError(ctl, _("cd: %s: %s"), strerror(errno), dir);
E
Eric Blake 已提交
14320
        ret = false;
P
Paolo Bonzini 已提交
14321 14322
    }

14323
    VIR_FREE(dir_malloced);
P
Phil Petty 已提交
14324
    return ret;
P
Paolo Bonzini 已提交
14325 14326 14327 14328 14329 14330
}

/*
 * "pwd" command
 */
static const vshCmdInfo info_pwd[] = {
14331 14332
    {"help", N_("print the current directory")},
    {"desc", N_("Print the current directory.")},
P
Paolo Bonzini 已提交
14333 14334 14335
    {NULL, NULL}
};

E
Eric Blake 已提交
14336
static bool
P
Paolo Bonzini 已提交
14337 14338 14339
cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *cwd;
14340
    bool ret = true;
P
Paolo Bonzini 已提交
14341

14342 14343
    cwd = getcwd(NULL, 0);
    if (!cwd) {
14344 14345
        vshError(ctl, _("pwd: cannot get current directory: %s"),
                 strerror(errno));
14346 14347
        ret = false;
    } else {
P
Paolo Bonzini 已提交
14348
        vshPrint (ctl, _("%s\n"), cwd);
14349 14350
        VIR_FREE(cwd);
    }
P
Paolo Bonzini 已提交
14351

14352
    return ret;
P
Paolo Bonzini 已提交
14353 14354
}

E
Eric Blake 已提交
14355 14356 14357 14358 14359 14360 14361 14362 14363 14364 14365 14366
/*
 * "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")},
14367
    {"string", VSH_OT_ARGV, 0, N_("arguments to echo")},
E
Eric Blake 已提交
14368 14369 14370 14371 14372 14373
    {NULL, 0, 0, NULL}
};

/* Exists mainly for debugging virsh, but also handy for adding back
 * quotes for later evaluation.
 */
E
Eric Blake 已提交
14374
static bool
14375
cmdEcho (vshControl *ctl, const vshCmd *cmd)
E
Eric Blake 已提交
14376 14377 14378 14379
{
    bool shell = false;
    bool xml = false;
    int count = 0;
14380
    const vshCmdOpt *opt = NULL;
E
Eric Blake 已提交
14381 14382 14383 14384 14385 14386 14387 14388
    char *arg;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

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

14389
    while ((opt = vshCommandOptArgv(cmd, opt))) {
14390 14391
        char *str;
        virBuffer xmlbuf = VIR_BUFFER_INITIALIZER;
E
Eric Blake 已提交
14392

14393
        arg = opt->data;
14394

E
Eric Blake 已提交
14395 14396
        if (count)
            virBufferAddChar(&buf, ' ');
14397

E
Eric Blake 已提交
14398
        if (xml) {
14399 14400 14401 14402
            virBufferEscapeString(&xmlbuf, "%s", arg);
            if (virBufferError(&buf)) {
                vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
                return false;
E
Eric Blake 已提交
14403
            }
14404 14405 14406
            str = virBufferContentAndReset(&xmlbuf);
        } else {
            str = vshStrdup(ctl, arg);
E
Eric Blake 已提交
14407
        }
14408 14409 14410 14411 14412

        if (shell)
            virBufferEscapeShell(&buf, str);
        else
            virBufferAdd(&buf, str, -1);
E
Eric Blake 已提交
14413
        count++;
14414
        VIR_FREE(str);
E
Eric Blake 已提交
14415 14416 14417 14418
    }

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
14419
        return false;
E
Eric Blake 已提交
14420 14421 14422 14423 14424
    }
    arg = virBufferContentAndReset(&buf);
    if (arg)
        vshPrint(ctl, "%s", arg);
    VIR_FREE(arg);
E
Eric Blake 已提交
14425
    return true;
E
Eric Blake 已提交
14426 14427
}

14428 14429 14430 14431
/*
 * "edit" command
 */
static const vshCmdInfo info_edit[] = {
14432 14433
    {"help", N_("edit XML configuration for a domain")},
    {"desc", N_("Edit the XML configuration for a domain.")},
14434 14435 14436 14437
    {NULL, NULL}
};

static const vshCmdOptDef opts_edit[] = {
14438
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
14439 14440 14441 14442 14443 14444
    {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 已提交
14445
static bool
14446 14447
cmdEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
14448
    bool ret = false;
14449 14450 14451 14452 14453
    virDomainPtr dom = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;
E
Eric Blake 已提交
14454
    unsigned int flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
14455

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

J
Jim Meyering 已提交
14459
    dom = vshCommandOptDomain (ctl, cmd, NULL);
14460 14461 14462 14463
    if (dom == NULL)
        goto cleanup;

    /* Get the XML configuration of the domain. */
14464
    doc = virDomainGetXMLDesc (dom, flags);
14465 14466 14467 14468 14469 14470 14471 14472 14473 14474 14475 14476 14477 14478 14479 14480 14481 14482
    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 已提交
14483
        ret = true;
14484 14485 14486 14487 14488 14489 14490
        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.
     */
14491
    doc_reread = virDomainGetXMLDesc (dom, flags);
14492 14493 14494 14495
    if (!doc_reread)
        goto cleanup;

    if (STRNEQ (doc, doc_reread)) {
14496 14497
        vshError(ctl,
                 "%s", _("ERROR: the XML configuration was changed by another user"));
14498 14499 14500 14501 14502 14503 14504 14505 14506 14507 14508 14509
        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 已提交
14510
    ret = true;
14511 14512 14513 14514 14515

 cleanup:
    if (dom)
        virDomainFree (dom);

14516 14517 14518
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);
14519 14520 14521

    if (tmp) {
        unlink (tmp);
14522
        VIR_FREE(tmp);
14523 14524 14525 14526 14527
    }

    return ret;
}

14528

14529 14530 14531 14532
/*
 * "net-edit" command
 */
static const vshCmdInfo info_network_edit[] = {
14533 14534
    {"help", N_("edit XML configuration for a network")},
    {"desc", N_("Edit the XML configuration for a network.")},
14535 14536 14537 14538
    {NULL, NULL}
};

static const vshCmdOptDef opts_network_edit[] = {
14539
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
14540 14541 14542 14543 14544 14545 14546 14547 14548 14549
    {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[] = {
14550 14551
    {"help", N_("edit XML configuration for a storage pool")},
    {"desc", N_("Edit the XML configuration for a storage pool.")},
14552 14553 14554 14555
    {NULL, NULL}
};

static const vshCmdOptDef opts_pool_edit[] = {
14556
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
14557 14558 14559 14560 14561 14562
    {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 已提交
14563 14564 14565
/*
 * "quit" command
 */
14566
static const vshCmdInfo info_quit[] = {
14567
    {"help", N_("quit this interactive terminal")},
14568
    {"desc", ""},
14569
    {NULL, NULL}
K
Karel Zak 已提交
14570 14571
};

E
Eric Blake 已提交
14572
static bool
14573
cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
14574
{
E
Eric Blake 已提交
14575 14576
    ctl->imode = false;
    return true;
K
Karel Zak 已提交
14577 14578
}

14579 14580 14581 14582 14583 14584 14585
/* 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;
14586
    bool halt = false;
14587 14588 14589
    char *doc = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
E
Eric Blake 已提交
14590
    const char *name = NULL;
14591 14592

    snapshot = virDomainSnapshotCreateXML(dom, buffer, flags);
14593 14594 14595 14596 14597 14598 14599 14600 14601 14602 14603 14604 14605 14606 14607 14608 14609 14610 14611 14612 14613 14614 14615 14616

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

14617 14618 14619
    if (snapshot == NULL)
        goto cleanup;

14620 14621 14622 14623 14624
    if (halt && virDomainDestroy(dom) < 0) {
        virshReportError(ctl);
        goto cleanup;
    }

E
Eric Blake 已提交
14625
    name = virDomainSnapshotGetName(snapshot);
14626
    if (!name) {
E
Eric Blake 已提交
14627
        vshError(ctl, "%s", _("Could not get snapshot name"));
14628 14629 14630 14631 14632 14633 14634 14635 14636 14637 14638 14639 14640 14641 14642 14643 14644 14645 14646
        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;
}

14647 14648 14649 14650
/*
 * "snapshot-create" command
 */
static const vshCmdInfo info_snapshot_create[] = {
E
Eric Blake 已提交
14651 14652
    {"help", N_("Create a snapshot from XML")},
    {"desc", N_("Create a snapshot (disk and RAM) from XML")},
14653 14654 14655 14656 14657 14658
    {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")},
14659 14660 14661
    {"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")},
14662
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
14663
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
14664
    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse any existing external files")},
14665
    {"quiesce", VSH_OT_BOOL, 0, N_("quiesce guest's file systems")},
14666 14667 14668
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
14669
static bool
14670 14671 14672
cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
14673
    bool ret = false;
14674
    const char *from = NULL;
14675
    char *buffer = NULL;
14676 14677 14678 14679 14680 14681 14682 14683
    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;
14684 14685
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
14686 14687
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
14688 14689
    if (vshCommandOptBool(cmd, "reuse-external"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT;
14690 14691
    if (vshCommandOptBool(cmd, "quiesce"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE;
14692

14693
    if (!vshConnectionUsability(ctl, ctl->conn))
14694 14695 14696 14697 14698 14699
        goto cleanup;

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

14700
    if (vshCommandOptString(cmd, "xmlfile", &from) <= 0)
E
Eric Blake 已提交
14701
        buffer = vshStrdup(ctl, "<domainsnapshot/>");
14702 14703 14704 14705 14706 14707 14708 14709 14710 14711 14712 14713 14714 14715 14716
    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;
    }

14717
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, from);
14718 14719 14720 14721 14722 14723 14724 14725 14726

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

    return ret;
}

14727 14728 14729
/*
 * "snapshot-create-as" command
 */
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
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;
}

14786 14787 14788 14789 14790 14791 14792 14793 14794 14795
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 已提交
14796
    {"print-xml", VSH_OT_BOOL, 0, N_("print XML document rather than create")},
14797
    {"no-metadata", VSH_OT_BOOL, 0, N_("take snapshot but create no metadata")},
14798
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
14799
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
14800
    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse any existing external files")},
14801
    {"quiesce", VSH_OT_BOOL, 0, N_("quiesce guest's file systems")},
14802 14803
    {"diskspec", VSH_OT_ARGV, 0,
     N_("disk attributes: disk[,snapshot=type][,driver=type][,file=name]")},
14804 14805 14806 14807 14808 14809 14810 14811 14812 14813 14814 14815
    {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;
14816
    unsigned int flags = 0;
14817
    const vshCmdOpt *opt = NULL;
14818 14819 14820

    if (vshCommandOptBool(cmd, "no-metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
14821 14822
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
14823 14824
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
14825 14826
    if (vshCommandOptBool(cmd, "reuse-external"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT;
14827 14828
    if (vshCommandOptBool(cmd, "quiesce"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE;
14829 14830 14831 14832 14833 14834 14835 14836 14837 14838 14839 14840 14841 14842 14843 14844

    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)
14845
        virBufferEscapeString(&buf, "  <name>%s</name>\n", name);
14846
    if (desc)
14847
        virBufferEscapeString(&buf, "  <description>%s</description>\n", desc);
14848 14849 14850 14851 14852 14853 14854 14855 14856 14857
    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");
    }
14858 14859 14860 14861 14862 14863 14864 14865
    virBufferAddLit(&buf, "</domainsnapshot>\n");

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

E
Eric Blake 已提交
14866 14867 14868 14869 14870 14871
    if (vshCommandOptBool(cmd, "print-xml")) {
        vshPrint(ctl, "%s\n",  buffer);
        ret = true;
        goto cleanup;
    }

14872
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, NULL);
14873 14874 14875 14876 14877 14878 14879 14880 14881

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

    return ret;
}

14882 14883 14884 14885 14886 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
/* 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;
}

14921 14922 14923 14924 14925 14926 14927 14928 14929 14930 14931
/*
 * "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")},
14932
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
14933
    {"current", VSH_OT_BOOL, 0, N_("also set edited snapshot as current")},
14934 14935
    {"rename", VSH_OT_BOOL, 0, N_("allow renaming an existing snapshot")},
    {"clone", VSH_OT_BOOL, 0, N_("allow cloning to new name")},
14936 14937 14938 14939 14940 14941 14942 14943
    {NULL, 0, 0, NULL}
};

static bool
cmdSnapshotEdit(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    virDomainSnapshotPtr snapshot = NULL;
14944
    virDomainSnapshotPtr edited = NULL;
14945
    const char *name;
14946
    const char *edited_name;
14947 14948 14949 14950 14951 14952
    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;
14953 14954 14955 14956 14957 14958 14959 14960
    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;
    }
14961

14962 14963
    if (vshCommandOptBool(cmd, "current") &&
        vshCommandOptBool(cmd, "snapshotname"))
14964 14965 14966 14967 14968 14969 14970 14971 14972
        define_flags |= VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;

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

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

14973 14974
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", false, dom,
                          &snapshot, &name) < 0)
14975 14976 14977 14978 14979 14980 14981
        goto cleanup;

    /* Get the XML configuration of the snapshot.  */
    doc = virDomainSnapshotGetXMLDesc(snapshot, getxml_flags);
    if (!doc)
        goto cleanup;

14982 14983 14984 14985
    /* 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;

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
    /* 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.  */
15011 15012
    edited = virDomainSnapshotCreateXML(dom, doc_edited, define_flags);
    if (!edited) {
15013 15014 15015 15016
        vshError(ctl, _("Failed to update %s"), name);
        goto cleanup;
    }

15017 15018 15019 15020 15021 15022 15023 15024 15025 15026 15027 15028 15029 15030 15031 15032 15033 15034 15035 15036 15037 15038 15039 15040
    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;
        }
    }

15041 15042 15043 15044 15045 15046 15047 15048 15049 15050 15051
    ret = true;

cleanup:
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    if (tmp) {
        unlink(tmp);
        VIR_FREE(tmp);
    }
    if (snapshot)
        virDomainSnapshotFree(snapshot);
15052 15053
    if (edited)
        virDomainSnapshotFree(edited);
15054 15055 15056 15057 15058
    if (dom)
        virDomainFree(dom);
    return ret;
}

15059 15060 15061 15062
/*
 * "snapshot-current" command
 */
static const vshCmdInfo info_snapshot_current[] = {
15063 15064
    {"help", N_("Get or set the current snapshot")},
    {"desc", N_("Get or set the current snapshot")},
15065 15066 15067 15068 15069
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_current[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
15070
    {"name", VSH_OT_BOOL, 0, N_("list the name, rather than the full xml")},
15071 15072
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
15073 15074
    {"snapshotname", VSH_OT_DATA, 0,
     N_("name of existing snapshot to make current")},
15075 15076 15077
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15078
static bool
15079 15080 15081
cmdSnapshotCurrent(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
15082
    bool ret = false;
15083 15084
    int current;
    virDomainSnapshotPtr snapshot = NULL;
15085
    char *xml = NULL;
15086
    const char *snapshotname = NULL;
15087
    unsigned int flags = 0;
15088
    const char *domname;
15089 15090 15091

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

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

15096
    dom = vshCommandOptDomain(ctl, cmd, &domname);
15097 15098 15099
    if (dom == NULL)
        goto cleanup;

15100 15101 15102 15103 15104 15105 15106 15107 15108 15109 15110 15111 15112 15113 15114 15115 15116 15117 15118 15119
    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;
15120 15121 15122
        /* 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;
15123 15124 15125 15126 15127 15128 15129 15130 15131
        snapshot2 = virDomainSnapshotCreateXML(dom, xml, flags);
        if (snapshot2 == NULL)
            goto cleanup;
        virDomainSnapshotFree(snapshot2);
        vshPrint(ctl, _("Snapshot %s set as current"), snapshotname);
        ret = true;
        goto cleanup;
    }

15132
    current = virDomainHasCurrentSnapshot(dom, 0);
15133 15134 15135 15136
    if (current < 0) {
        goto cleanup;
    } else if (!current) {
        vshError(ctl, _("domain '%s' has no current snapshot"), domname);
15137
        goto cleanup;
15138
    } else {
E
Eric Blake 已提交
15139
        const char *name = NULL;
15140 15141 15142 15143

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

15144
        if (vshCommandOptBool(cmd, "name")) {
E
Eric Blake 已提交
15145
            name = virDomainSnapshotGetName(snapshot);
15146 15147
            if (!name)
                goto cleanup;
E
Eric Blake 已提交
15148 15149 15150 15151
        } else {
            xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
            if (!xml)
                goto cleanup;
15152 15153 15154
        }

        vshPrint(ctl, "%s", name ? name : xml);
15155 15156
    }

E
Eric Blake 已提交
15157
    ret = true;
15158 15159

cleanup:
15160 15161
    if (!ret)
        virshReportError(ctl);
15162
    VIR_FREE(xml);
15163 15164 15165 15166 15167 15168 15169 15170
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

15171
/* Helper function to get the name of a snapshot's parent.  Caller
15172 15173 15174 15175 15176 15177
 * 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)
15178 15179 15180 15181 15182
{
    virDomainSnapshotPtr parent = NULL;
    char *xml = NULL;
    xmlDocPtr xmldoc = NULL;
    xmlXPathContextPtr ctxt = NULL;
15183 15184 15185
    int ret = -1;

    *parent_name = NULL;
15186 15187

    /* Try new API, since it is faster. */
15188
    if (!ctl->useSnapshotOld) {
15189 15190 15191
        parent = virDomainSnapshotGetParent(snapshot, 0);
        if (parent) {
            /* API works, and virDomainSnapshotGetName will succeed */
15192 15193
            *parent_name = vshStrdup(ctl, virDomainSnapshotGetName(parent));
            ret = 0;
15194 15195 15196 15197
            goto cleanup;
        }
        if (last_error->code == VIR_ERR_NO_DOMAIN_SNAPSHOT) {
            /* API works, and we found a root with no parent */
15198
            ret = 0;
15199 15200 15201
            goto cleanup;
        }
        /* API didn't work, fall back to XML scraping. */
15202
        ctl->useSnapshotOld = true;
15203 15204 15205 15206 15207 15208 15209 15210 15211 15212
    }

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

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

15213 15214
    *parent_name = virXPathString("string(/domainsnapshot/parent/name)", ctxt);
    ret = 0;
15215 15216

cleanup:
15217 15218 15219 15220 15221 15222 15223
    if (ret < 0) {
        virshReportError(ctl);
        vshError(ctl, "%s", _("unable to determine if snapshot has parent"));
    } else {
        virFreeError(last_error);
        last_error = NULL;
    }
15224 15225 15226 15227 15228
    if (parent)
        virDomainSnapshotFree(parent);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xmldoc);
    VIR_FREE(xml);
15229
    return ret;
15230 15231
}

15232 15233 15234 15235 15236 15237 15238 15239 15240 15241 15242
/*
 * "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")},
15243
    {"parent", VSH_OT_BOOL, 0, N_("add a column showing parent snapshot")},
15244
    {"roots", VSH_OT_BOOL, 0, N_("list only snapshots without parents")},
15245
    {"leaves", VSH_OT_BOOL, 0, N_("list only snapshots without children")},
15246 15247
    {"metadata", VSH_OT_BOOL, 0,
     N_("list only snapshots that have metadata that would prevent undefine")},
15248
    {"tree", VSH_OT_BOOL, 0, N_("list snapshots in a tree")},
15249
    {"from", VSH_OT_DATA, 0, N_("limit list to children of given snapshot")},
15250 15251
    {"current", VSH_OT_BOOL, 0,
     N_("limit list to children of current snapshot")},
15252
    {"descendants", VSH_OT_BOOL, 0, N_("with --from, list all descendants")},
15253 15254 15255
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15256
static bool
15257 15258 15259
cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
15260
    bool ret = false;
15261
    unsigned int flags = 0;
15262 15263
    int parent_filter = 0; /* -1 for roots filtering, 0 for no parent
                              information needed, 1 for parent column */
15264 15265
    int numsnaps;
    char **names = NULL;
15266
    char **parents = NULL;
15267
    int actual = 0;
15268 15269 15270 15271 15272 15273
    int i;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    char *doc = NULL;
    virDomainSnapshotPtr snapshot = NULL;
    char *state = NULL;
15274
    char *parent = NULL;
15275 15276
    long long creation_longlong;
    time_t creation_time_t;
15277 15278
    char timestr[100];
    struct tm time_info;
15279
    bool tree = vshCommandOptBool(cmd, "tree");
15280
    bool leaves = vshCommandOptBool(cmd, "leaves");
15281 15282
    const char *from = NULL;
    virDomainSnapshotPtr start = NULL;
15283
    int start_index = -1;
15284
    bool descendants = false;
15285

15286 15287 15288 15289 15290 15291 15292 15293 15294 15295
    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)
15296
        goto cleanup;
15297

15298
    if (vshCommandOptBool(cmd, "parent")) {
15299 15300
        if (vshCommandOptBool(cmd, "roots")) {
            vshError(ctl, "%s",
15301
                     _("--parent and --roots are mutually exclusive"));
15302 15303
            return false;
        }
15304 15305
        if (tree) {
            vshError(ctl, "%s",
15306
                     _("--parent and --tree are mutually exclusive"));
15307 15308
            return false;
        }
15309
        parent_filter = 1;
15310
    } else if (vshCommandOptBool(cmd, "roots")) {
15311 15312
        if (tree) {
            vshError(ctl, "%s",
15313
                     _("--roots and --tree are mutually exclusive"));
15314 15315
            return false;
        }
15316 15317 15318 15319
        if (from) {
            vshError(ctl, "%s",
                     _("--roots and --from are mutually exclusive"));
        }
15320 15321
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
    }
15322 15323 15324 15325 15326 15327 15328 15329
    if (leaves) {
        if (tree) {
            vshError(ctl, "%s",
                     _("--leaves and --tree are mutually exclusive"));
            return false;
        }
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_LEAVES;
    }
15330 15331 15332

    if (vshCommandOptBool(cmd, "metadata")) {
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_METADATA;
15333 15334
    }

15335
    if (from) {
15336 15337
        descendants = vshCommandOptBool(cmd, "descendants");
        if (descendants || tree) {
15338 15339
            flags |= VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
        }
15340 15341
        numsnaps = ctl->useSnapshotOld ? -1 :
            virDomainSnapshotNumChildren(start, flags);
15342
        if (numsnaps < 0) {
15343 15344
            if (ctl->useSnapshotOld ||
                last_error->code == VIR_ERR_NO_SUPPORT) {
15345
                /* We can emulate --from.  */
15346
                /* XXX can we also emulate --leaves? */
15347 15348 15349 15350 15351 15352
                virFreeError(last_error);
                last_error = NULL;
                ctl->useSnapshotOld = true;
                flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
                numsnaps = virDomainSnapshotNum(dom, flags);
            }
15353 15354
        } else if (tree) {
            numsnaps++;
15355
        }
15356
    } else {
15357
        numsnaps = virDomainSnapshotNum(dom, flags);
15358 15359

        /* Fall back to simulation if --roots was unsupported. */
15360
        /* XXX can we also emulate --leaves? */
15361 15362 15363 15364 15365 15366 15367 15368
        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);
        }
15369 15370
    }

15371 15372 15373
    if (numsnaps < 0) {
        if (!last_error)
            vshError(ctl, _("missing support"));
15374
        goto cleanup;
15375
    }
15376

15377 15378 15379 15380 15381 15382 15383 15384 15385
    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\
15386
------------------------------------------------------------\n");
15387
    }
15388

15389 15390 15391 15392
    if (!numsnaps) {
        ret = true;
        goto cleanup;
    }
15393

15394 15395 15396
    if (VIR_ALLOC_N(names, numsnaps) < 0)
        goto cleanup;

15397
    if (from && !ctl->useSnapshotOld) {
15398 15399 15400 15401 15402 15403 15404 15405 15406 15407 15408 15409 15410 15411 15412 15413 15414 15415
        /* 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);
    }
15416 15417
    if (actual < 0)
        goto cleanup;
15418

E
Eric Blake 已提交
15419 15420
    qsort(&names[0], actual, sizeof(char*), namesorter);

15421 15422
    if (tree || ctl->useSnapshotOld) {
        parents = vshCalloc(ctl, sizeof(char *), actual);
15423
        for (i = (from && !ctl->useSnapshotOld); i < actual; i++) {
15424 15425 15426 15427 15428
            if (ctl->useSnapshotOld && STREQ(names[i], from)) {
                start_index = i;
                continue;
            }

15429 15430 15431 15432
            /* free up memory from previous iterations of the loop */
            if (snapshot)
                virDomainSnapshotFree(snapshot);
            snapshot = virDomainSnapshotLookupByName(dom, names[i], 0);
15433 15434
            if (!snapshot ||
                vshGetSnapshotParent(ctl, snapshot, &parents[i]) < 0) {
15435 15436 15437 15438 15439 15440
                while (--i >= 0)
                    VIR_FREE(parents[i]);
                VIR_FREE(parents);
                goto cleanup;
            }
        }
15441 15442 15443
    }
    if (tree) {
        char indentBuf[INDENT_BUFLEN];
15444 15445
        for (i = 0 ; i < actual ; i++) {
            memset(indentBuf, '\0', sizeof indentBuf);
15446
            if (ctl->useSnapshotOld ? STREQ(names[i], from) : !parents[i])
15447 15448 15449 15450 15451 15452 15453 15454 15455 15456 15457 15458 15459 15460
                cmdNodeListDevicesPrint(ctl,
                                        names,
                                        parents,
                                        actual,
                                        i,
                                        i,
                                        0,
                                        0,
                                        indentBuf);
        }

        ret = true;
        goto cleanup;
    } else {
15461
        if (ctl->useSnapshotOld && descendants) {
15462 15463 15464 15465 15466 15467 15468 15469 15470 15471 15472 15473 15474 15475 15476 15477 15478 15479 15480 15481 15482 15483 15484 15485 15486 15487 15488 15489 15490 15491 15492 15493 15494 15495 15496 15497 15498 15499 15500 15501 15502 15503 15504 15505 15506 15507 15508 15509 15510 15511
            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]);
15512 15513
        }

15514
        for (i = 0; i < actual; i++) {
15515 15516
            if (ctl->useSnapshotOld &&
                (descendants ? !names[i] : STRNEQ_NULLABLE(parents[i], from)))
15517 15518
                continue;

15519
            /* free up memory from previous iterations of the loop */
15520
            VIR_FREE(parent);
15521 15522 15523 15524
            VIR_FREE(state);
            if (snapshot)
                virDomainSnapshotFree(snapshot);
            xmlXPathFreeContext(ctxt);
15525
            xmlFreeDoc(xml);
15526 15527 15528 15529 15530 15531 15532 15533 15534 15535
            VIR_FREE(doc);

            snapshot = virDomainSnapshotLookupByName(dom, names[i], 0);
            if (snapshot == NULL)
                continue;

            doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
            if (!doc)
                continue;

15536
            xml = virXMLParseStringCtxt(doc, _("(domain_snapshot)"), &ctxt);
15537 15538 15539
            if (!xml)
                continue;

15540 15541 15542
            if (parent_filter) {
                parent = virXPathString("string(/domainsnapshot/parent/name)",
                                        ctxt);
15543 15544
                if (!parent && parent_filter < 0)
                    continue;
15545 15546
            }

15547 15548 15549
            state = virXPathString("string(/domainsnapshot/state)", ctxt);
            if (state == NULL)
                continue;
15550 15551
            if (virXPathLongLong("string(/domainsnapshot/creationTime)", ctxt,
                                 &creation_longlong) < 0)
15552
                continue;
15553 15554 15555 15556 15557 15558
            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);
15559 15560
            strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S %z",
                     &time_info);
15561

15562 15563 15564 15565 15566
            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);
15567 15568 15569
        }
    }

E
Eric Blake 已提交
15570
    ret = true;
15571 15572 15573

cleanup:
    /* this frees up memory from the last iteration of the loop */
15574
    VIR_FREE(parent);
15575 15576 15577
    VIR_FREE(state);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
15578 15579
    if (start)
        virDomainSnapshotFree(start);
15580
    xmlXPathFreeContext(ctxt);
15581
    xmlFreeDoc(xml);
15582
    VIR_FREE(doc);
15583
    for (i = 0; i < actual; i++) {
15584
        VIR_FREE(names[i]);
15585 15586 15587
        if (parents)
            VIR_FREE(parents[i]);
    }
15588
    VIR_FREE(names);
15589
    VIR_FREE(parents);
15590 15591 15592 15593 15594 15595 15596 15597 15598 15599 15600 15601 15602 15603 15604 15605 15606 15607
    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")},
15608 15609
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
15610 15611 15612
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15613
static bool
15614 15615 15616
cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
15617
    bool ret = false;
15618
    const char *name = NULL;
15619 15620
    virDomainSnapshotPtr snapshot = NULL;
    char *xml = NULL;
15621 15622 15623 15624
    unsigned int flags = 0;

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

15626
    if (!vshConnectionUsability(ctl, ctl->conn))
15627 15628 15629 15630 15631 15632
        goto cleanup;

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

15633
    if (vshCommandOptString(cmd, "snapshotname", &name) <= 0)
15634 15635 15636 15637 15638 15639
        goto cleanup;

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

15640
    xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
15641 15642 15643
    if (!xml)
        goto cleanup;

15644
    vshPrint(ctl, "%s", xml);
15645

E
Eric Blake 已提交
15646
    ret = true;
15647 15648 15649 15650 15651 15652 15653 15654 15655 15656 15657

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

    return ret;
}

E
Eric Blake 已提交
15658 15659 15660 15661
/*
 * "snapshot-parent" command
 */
static const vshCmdInfo info_snapshot_parent[] = {
E
Eric Blake 已提交
15662
    {"help", N_("Get the name of the parent of a snapshot")},
E
Eric Blake 已提交
15663 15664 15665 15666 15667 15668
    {"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")},
15669 15670
    {"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 已提交
15671 15672 15673 15674 15675 15676 15677 15678 15679 15680 15681 15682 15683 15684 15685 15686 15687 15688 15689
    {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;

15690 15691
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
E
Eric Blake 已提交
15692 15693
        goto cleanup;

15694
    if (vshGetSnapshotParent(ctl, snapshot, &parent) < 0)
E
Eric Blake 已提交
15695
        goto cleanup;
15696 15697 15698 15699
    if (!parent) {
        vshError(ctl, _("snapshot '%s' has no parent"), name);
        goto cleanup;
    }
E
Eric Blake 已提交
15700 15701 15702 15703 15704 15705 15706 15707 15708 15709 15710 15711 15712 15713 15714

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

    ret = true;

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

    return ret;
}

15715
/*
15716
 * "snapshot-revert" command
15717
 */
15718
static const vshCmdInfo info_snapshot_revert[] = {
15719 15720 15721 15722 15723
    {"help", N_("Revert a domain to a snapshot")},
    {"desc", N_("Revert domain to snapshot")},
    {NULL, NULL}
};

15724
static const vshCmdOptDef opts_snapshot_revert[] = {
15725
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
15726 15727
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("revert to current snapshot")},
15728 15729
    {"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 已提交
15730
    {"force", VSH_OT_BOOL, 0, N_("try harder on risky reverts")},
15731 15732 15733
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15734
static bool
15735
cmdDomainSnapshotRevert(vshControl *ctl, const vshCmd *cmd)
15736 15737
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
15738
    bool ret = false;
15739
    const char *name = NULL;
15740
    virDomainSnapshotPtr snapshot = NULL;
15741
    unsigned int flags = 0;
E
Eric Blake 已提交
15742 15743
    bool force = false;
    int result;
15744 15745 15746 15747 15748

    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED;
E
Eric Blake 已提交
15749 15750 15751 15752 15753 15754
    /* 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;
15755

15756
    if (!vshConnectionUsability(ctl, ctl->conn))
15757 15758 15759 15760 15761 15762
        goto cleanup;

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

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

E
Eric Blake 已提交
15767 15768 15769 15770 15771 15772 15773 15774 15775
    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)
15776 15777
        goto cleanup;

E
Eric Blake 已提交
15778
    ret = true;
15779 15780 15781 15782 15783 15784 15785 15786 15787 15788 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 15799

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")},
15800 15801
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("delete current snapshot")},
15802
    {"children", VSH_OT_BOOL, 0, N_("delete snapshot and all children")},
15803 15804 15805
    {"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")},
15806 15807 15808
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15809
static bool
15810 15811 15812
cmdSnapshotDelete(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
15813
    bool ret = false;
15814
    const char *name = NULL;
15815 15816 15817
    virDomainSnapshotPtr snapshot = NULL;
    unsigned int flags = 0;

15818
    if (!vshConnectionUsability(ctl, ctl->conn))
15819 15820 15821 15822 15823 15824
        goto cleanup;

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

15825 15826
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
15827 15828 15829 15830
        goto cleanup;

    if (vshCommandOptBool(cmd, "children"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN;
15831 15832 15833 15834
    if (vshCommandOptBool(cmd, "children-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY;
    if (vshCommandOptBool(cmd, "metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY;
15835

15836 15837 15838
    /* 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.  */
15839
    if (virDomainSnapshotDelete(snapshot, flags) == 0) {
15840 15841 15842 15843
        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);
15844 15845
    } else {
        vshError(ctl, _("Failed to delete snapshot %s"), name);
15846
        goto cleanup;
15847
    }
15848

E
Eric Blake 已提交
15849
    ret = true;
15850 15851 15852 15853 15854 15855 15856 15857 15858 15859

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

    return ret;
}

15860 15861 15862 15863
/*
 * "qemu-monitor-command" command
 */
static const vshCmdInfo info_qemu_monitor_command[] = {
15864 15865
    {"help", N_("QEMU Monitor Command")},
    {"desc", N_("QEMU Monitor Command")},
15866 15867 15868 15869 15870
    {NULL, NULL}
};

static const vshCmdOptDef opts_qemu_monitor_command[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
15871
    {"hmp", VSH_OT_BOOL, 0, N_("command is in human monitor protocol")},
15872
    {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")},
15873 15874 15875
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
15876
static bool
15877 15878 15879
cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
15880
    bool ret = false;
15881
    char *monitor_cmd = NULL;
15882
    char *result = NULL;
15883
    unsigned int flags = 0;
15884 15885 15886
    const vshCmdOpt *opt = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    bool pad = false;
15887 15888 15889 15890 15891 15892 15893 15894

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

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

15895 15896 15897 15898 15899 15900 15901 15902
    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"));
15903 15904
        goto cleanup;
    }
15905
    monitor_cmd = virBufferContentAndReset(&buf);
15906

15907 15908 15909 15910
    if (vshCommandOptBool(cmd, "hmp"))
        flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP;

    if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0)
15911 15912 15913 15914
        goto cleanup;

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

E
Eric Blake 已提交
15915
    ret = true;
15916 15917 15918

cleanup:
    VIR_FREE(result);
15919
    VIR_FREE(monitor_cmd);
15920 15921 15922 15923 15924 15925
    if (dom)
        virDomainFree(dom);

    return ret;
}

15926 15927 15928 15929 15930 15931 15932 15933 15934 15935 15936 15937 15938 15939 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
/*
 * "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;
    unsigned int pid;

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

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

    if (!(dom = virDomainQemuAttach(ctl->conn, pid, flags)))
        goto cleanup;

    if (dom != NULL) {
        vshPrint(ctl, _("Domain %s attached to pid %u\n"),
                 virDomainGetName(dom), pid);
        virDomainFree(dom);
        ret = true;
    } else {
        vshError(ctl, _("Failed to attach to pid %u"), pid);
    }

cleanup:
    return ret;
}

15972
static const vshCmdDef domManagementCmds[] = {
15973 15974 15975 15976 15977 15978 15979
    {"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 已提交
15980
    {"blkdeviotune", cmdBlkdeviotune, opts_blkdeviotune, info_blkdeviotune, 0},
15981
    {"blkiotune", cmdBlkiotune, opts_blkiotune, info_blkiotune, 0},
15982 15983
    {"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0},
    {"blockjob", cmdBlockJob, opts_block_job, info_block_job, 0},
15984
    {"blockresize", cmdBlockResize, opts_block_resize, info_block_resize, 0},
15985
#ifndef WIN32
15986
    {"console", cmdConsole, opts_console, info_console, 0},
15987
#endif
15988 15989 15990 15991 15992 15993 15994 15995 15996 15997 15998
    {"cpu-baseline", cmdCPUBaseline, opts_cpu_baseline, info_cpu_baseline, 0},
    {"cpu-compare", cmdCPUCompare, opts_cpu_compare, info_cpu_compare, 0},
    {"create", cmdCreate, opts_create, info_create, 0},
    {"define", cmdDefine, opts_define, info_define, 0},
    {"destroy", cmdDestroy, opts_destroy, info_destroy, 0},
    {"detach-device", cmdDetachDevice, opts_detach_device,
     info_detach_device, 0},
    {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk, 0},
    {"detach-interface", cmdDetachInterface, opts_detach_interface,
     info_detach_interface, 0},
    {"domid", cmdDomid, opts_domid, info_domid, 0},
15999
    {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink, 0},
16000
    {"domiftune", cmdDomIftune, opts_domiftune, info_domiftune, 0},
16001 16002 16003 16004 16005 16006 16007 16008 16009 16010 16011 16012
    {"domjobabort", cmdDomjobabort, opts_domjobabort, info_domjobabort, 0},
    {"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo, 0},
    {"domname", cmdDomname, opts_domname, info_domname, 0},
    {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid, 0},
    {"domxml-from-native", cmdDomXMLFromNative, opts_domxmlfromnative,
     info_domxmlfromnative, 0},
    {"domxml-to-native", cmdDomXMLToNative, opts_domxmltonative,
     info_domxmltonative, 0},
    {"dump", cmdDump, opts_dump, info_dump, 0},
    {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0},
    {"edit", cmdEdit, opts_edit, info_edit, 0},
    {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0},
16013
    {"send-key", cmdSendKey, opts_send_key, info_send_key},
16014 16015 16016 16017 16018 16019 16020 16021
    {"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},
16022 16023
    {"migrate-setspeed", cmdMigrateSetMaxSpeed,
     opts_migrate_setspeed, info_migrate_setspeed, 0},
16024 16025
    {"migrate-getspeed", cmdMigrateGetMaxSpeed,
     opts_migrate_getspeed, info_migrate_getspeed, 0},
H
Hu Tao 已提交
16026
    {"numatune", cmdNumatune, opts_numatune, info_numatune, 0},
16027
    {"reboot", cmdReboot, opts_reboot, info_reboot, 0},
X
Xu He Jie 已提交
16028
    {"reset", cmdReset, opts_reset, info_reset, 0},
16029 16030 16031
    {"restore", cmdRestore, opts_restore, info_restore, 0},
    {"resume", cmdResume, opts_resume, info_resume, 0},
    {"save", cmdSave, opts_save, info_save, 0},
16032 16033 16034 16035 16036 16037
    {"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},
16038
    {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo, 0},
16039
    {"screenshot", cmdScreenshot, opts_screenshot, info_screenshot, 0},
16040 16041 16042 16043 16044 16045 16046 16047 16048 16049 16050 16051
    {"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 已提交
16052
    {"vcpupin", cmdVcpuPin, opts_vcpupin, info_vcpupin, 0},
16053 16054
    {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay, 0},
    {NULL, NULL, NULL, NULL, 0}
16055 16056 16057
};

static const vshCmdDef domMonitoringCmds[] = {
16058
    {"domblkinfo", cmdDomblkinfo, opts_domblkinfo, info_domblkinfo, 0},
16059
    {"domblklist", cmdDomblklist, opts_domblklist, info_domblklist, 0},
16060
    {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat, 0},
16061
    {"domcontrol", cmdDomControl, opts_domcontrol, info_domcontrol, 0},
16062
    {"domif-getlink", cmdDomIfGetLink, opts_domif_getlink, info_domif_getlink, 0},
16063
    {"domiflist", cmdDomiflist, opts_domiflist, info_domiflist, 0},
16064 16065 16066 16067 16068 16069
    {"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}
16070 16071 16072
};

static const vshCmdDef storagePoolCmds[] = {
16073
    {"find-storage-pool-sources-as", cmdPoolDiscoverSourcesAs,
16074
     opts_find_storage_pool_sources_as, info_find_storage_pool_sources_as, 0},
16075
    {"find-storage-pool-sources", cmdPoolDiscoverSources,
16076 16077 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094 16095 16096
     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}
16097 16098 16099
};

static const vshCmdDef storageVolCmds[] = {
16100 16101 16102 16103 16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 16117
    {"vol-clone", cmdVolClone, opts_vol_clone, info_vol_clone, 0},
    {"vol-create-as", cmdVolCreateAs, opts_vol_create_as,
     info_vol_create_as, 0},
    {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create, 0},
    {"vol-create-from", cmdVolCreateFrom, opts_vol_create_from,
     info_vol_create_from, 0},
    {"vol-delete", cmdVolDelete, opts_vol_delete, info_vol_delete, 0},
    {"vol-download", cmdVolDownload, opts_vol_download, info_vol_download, 0},
    {"vol-dumpxml", cmdVolDumpXML, opts_vol_dumpxml, info_vol_dumpxml, 0},
    {"vol-info", cmdVolInfo, opts_vol_info, info_vol_info, 0},
    {"vol-key", cmdVolKey, opts_vol_key, info_vol_key, 0},
    {"vol-list", cmdVolList, opts_vol_list, info_vol_list, 0},
    {"vol-name", cmdVolName, opts_vol_name, info_vol_name, 0},
    {"vol-path", cmdVolPath, opts_vol_path, info_vol_path, 0},
    {"vol-pool", cmdVolPool, opts_vol_pool, info_vol_pool, 0},
    {"vol-upload", cmdVolUpload, opts_vol_upload, info_vol_upload, 0},
    {"vol-wipe", cmdVolWipe, opts_vol_wipe, info_vol_wipe, 0},
    {NULL, NULL, NULL, NULL, 0}
16118
};
16119

16120
static const vshCmdDef networkCmds[] = {
16121 16122 16123 16124 16125 16126 16127 16128 16129 16130 16131 16132 16133 16134 16135 16136 16137 16138 16139
    {"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}
16140
};
16141

16142
static const vshCmdDef nodedevCmds[] = {
16143 16144 16145 16146 16147 16148 16149 16150 16151 16152 16153 16154 16155 16156 16157
    {"nodedev-create", cmdNodeDeviceCreate, opts_node_device_create,
     info_node_device_create, 0},
    {"nodedev-destroy", cmdNodeDeviceDestroy, opts_node_device_destroy,
     info_node_device_destroy, 0},
    {"nodedev-dettach", cmdNodeDeviceDettach, opts_node_device_dettach,
     info_node_device_dettach, 0},
    {"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}
16158 16159 16160
};

static const vshCmdDef ifaceCmds[] = {
E
Eric Blake 已提交
16161 16162
    {"iface-begin", cmdInterfaceBegin, opts_interface_begin,
     info_interface_begin, 0},
16163 16164
    {"iface-bridge", cmdInterfaceBridge, opts_interface_bridge,
     info_interface_bridge, 0},
E
Eric Blake 已提交
16165 16166
    {"iface-commit", cmdInterfaceCommit, opts_interface_commit,
     info_interface_commit, 0},
16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180
    {"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 已提交
16181 16182
    {"iface-rollback", cmdInterfaceRollback, opts_interface_rollback,
     info_interface_rollback, 0},
16183 16184
    {"iface-start", cmdInterfaceStart, opts_interface_start,
     info_interface_start, 0},
16185 16186
    {"iface-unbridge", cmdInterfaceUnbridge, opts_interface_unbridge,
     info_interface_unbridge, 0},
16187 16188 16189
    {"iface-undefine", cmdInterfaceUndefine, opts_interface_undefine,
     info_interface_undefine, 0},
    {NULL, NULL, NULL, NULL, 0}
16190
};
16191

16192
static const vshCmdDef nwfilterCmds[] = {
16193 16194 16195 16196 16197 16198 16199 16200 16201 16202 16203
    {"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}
16204
};
16205

16206
static const vshCmdDef secretCmds[] = {
16207 16208 16209 16210 16211 16212 16213 16214 16215 16216 16217 16218
    {"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}
16219
};
16220

16221
static const vshCmdDef virshCmds[] = {
16222 16223 16224 16225 16226 16227 16228
    {"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}
16229
};
16230

16231
static const vshCmdDef snapshotCmds[] = {
16232 16233
    {"snapshot-create", cmdSnapshotCreate, opts_snapshot_create,
     info_snapshot_create, 0},
16234 16235
    {"snapshot-create-as", cmdSnapshotCreateAs, opts_snapshot_create_as,
     info_snapshot_create_as, 0},
16236 16237 16238 16239 16240 16241
    {"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},
16242 16243
    {"snapshot-edit", cmdSnapshotEdit, opts_snapshot_edit,
     info_snapshot_edit, 0},
16244 16245
    {"snapshot-list", cmdSnapshotList, opts_snapshot_list,
     info_snapshot_list, 0},
E
Eric Blake 已提交
16246 16247
    {"snapshot-parent", cmdSnapshotParent, opts_snapshot_parent,
     info_snapshot_parent, 0},
16248 16249 16250
    {"snapshot-revert", cmdDomainSnapshotRevert, opts_snapshot_revert,
     info_snapshot_revert, 0},
    {NULL, NULL, NULL, NULL, 0}
16251
};
16252

16253
static const vshCmdDef hostAndHypervisorCmds[] = {
16254 16255 16256 16257 16258
    {"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},
16259
    {"nodecpustats", cmdNodeCpuStats, opts_node_cpustats, info_nodecpustats, 0},
16260
    {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo, 0},
16261
    {"nodememstats", cmdNodeMemStats, opts_node_memstats, info_nodememstats, 0},
16262
    {"nodesuspend", cmdNodeSuspend, opts_node_suspend, info_nodesuspend, 0},
16263
    {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach},
16264 16265 16266 16267
    {"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},
16268
    {"version", cmdVersion, opts_version, info_version, 0},
16269
    {NULL, NULL, NULL, NULL, 0}
K
Karel Zak 已提交
16270 16271
};

16272 16273 16274 16275 16276 16277 16278 16279 16280 16281 16282 16283 16284 16285 16286 16287 16288
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 已提交
16289 16290 16291 16292
/* ---------------
 * Utils for work with command definition
 * ---------------
 */
K
Karel Zak 已提交
16293
static const char *
16294
vshCmddefGetInfo(const vshCmdDef * cmd, const char *name)
16295
{
16296
    const vshCmdInfo *info;
16297

K
Karel Zak 已提交
16298
    for (info = cmd->info; info && info->name; info++) {
16299
        if (STREQ(info->name, name))
K
Karel Zak 已提交
16300 16301 16302 16303 16304
            return info->data;
    }
    return NULL;
}

16305
/* Validate that the options associated with cmd can be parsed.  */
16306
static int
E
Eric Blake 已提交
16307
vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg,
16308 16309 16310 16311 16312
                  uint32_t *opts_required)
{
    int i;
    bool optional = false;

E
Eric Blake 已提交
16313 16314 16315
    *opts_need_arg = 0;
    *opts_required = 0;

16316 16317 16318 16319 16320 16321 16322 16323 16324
    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 已提交
16325
            if (opt->flags & VSH_OFLAG_REQ)
16326 16327 16328
                return -1; /* bool options can't be mandatory */
            continue;
        }
E
Eric Blake 已提交
16329 16330
        if (opt->flags & VSH_OFLAG_REQ_OPT) {
            if (opt->flags & VSH_OFLAG_REQ)
L
Lai Jiangshan 已提交
16331 16332 16333 16334
                *opts_required |= 1 << i;
            continue;
        }

16335
        *opts_need_arg |= 1 << i;
E
Eric Blake 已提交
16336
        if (opt->flags & VSH_OFLAG_REQ) {
16337 16338 16339 16340 16341 16342
            if (optional)
                return -1; /* mandatory options must be listed first */
            *opts_required |= 1 << i;
        } else {
            optional = true;
        }
16343 16344 16345

        if (opt->type == VSH_OT_ARGV && cmd->opts[i + 1].name)
            return -1; /* argv option must be listed last */
16346 16347 16348 16349
    }
    return 0;
}

16350
static const vshCmdOptDef *
16351
vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
16352
                   uint32_t *opts_seen, int *opt_index)
16353
{
16354 16355 16356 16357
    int i;

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

16359
        if (STREQ(opt->name, name)) {
16360
            if ((*opts_seen & (1 << i)) && opt->type != VSH_OT_ARGV) {
16361 16362 16363
                vshError(ctl, _("option --%s already seen"), name);
                return NULL;
            }
16364 16365
            *opts_seen |= 1 << i;
            *opt_index = i;
K
Karel Zak 已提交
16366
            return opt;
16367 16368 16369 16370 16371
        }
    }

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

16375
static const vshCmdOptDef *
16376 16377
vshCmddefGetData(const vshCmdDef *cmd, uint32_t *opts_need_arg,
                 uint32_t *opts_seen)
16378
{
16379
    int i;
16380
    const vshCmdOptDef *opt;
K
Karel Zak 已提交
16381

16382 16383 16384 16385
    if (!*opts_need_arg)
        return NULL;

    /* Grab least-significant set bit */
E
Eric Blake 已提交
16386
    i = ffs(*opts_need_arg) - 1;
16387
    opt = &cmd->opts[i];
16388
    if (opt->type != VSH_OT_ARGV)
16389
        *opts_need_arg &= ~(1 << i);
16390
    *opts_seen |= 1 << i;
16391
    return opt;
K
Karel Zak 已提交
16392 16393
}

16394 16395 16396
/*
 * Checks for required options
 */
16397
static int
16398 16399
vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required,
                    uint32_t opts_seen)
16400
{
16401
    const vshCmdDef *def = cmd->def;
16402 16403 16404 16405 16406 16407 16408 16409 16410
    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];
16411

16412
            vshError(ctl,
16413
                     opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ?
16414 16415 16416
                     _("command '%s' requires <%s> option") :
                     _("command '%s' requires --%s option"),
                     def->name, opt->name);
16417 16418
        }
    }
16419
    return -1;
16420 16421
}

16422
static const vshCmdDef *
16423 16424
vshCmddefSearch(const char *cmdname)
{
16425
    const vshCmdGrp *g;
16426
    const vshCmdDef *c;
16427

16428 16429
    for (g = cmdGroups; g->name; g++) {
        for (c = g->commands; c->name; c++) {
16430
            if (STREQ(c->name, cmdname))
16431 16432 16433 16434
                return c;
        }
    }

K
Karel Zak 已提交
16435 16436 16437
    return NULL;
}

16438 16439 16440 16441 16442 16443
static const vshCmdGrp *
vshCmdGrpSearch(const char *grpname)
{
    const vshCmdGrp *g;

    for (g = cmdGroups; g->name; g++) {
16444
        if (STREQ(g->name, grpname) || STREQ(g->keyword, grpname))
16445 16446 16447 16448 16449 16450
            return g;
    }

    return NULL;
}

E
Eric Blake 已提交
16451
static bool
16452 16453 16454 16455 16456 16457 16458
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 已提交
16459
        return false;
16460 16461 16462 16463 16464 16465 16466 16467 16468 16469
    } 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 已提交
16470
    return true;
16471 16472
}

E
Eric Blake 已提交
16473
static bool
16474
vshCmddefHelp(vshControl *ctl, const char *cmdname)
16475
{
16476
    const vshCmdDef *def = vshCmddefSearch(cmdname);
16477

K
Karel Zak 已提交
16478
    if (!def) {
16479
        vshError(ctl, _("command '%s' doesn't exist"), cmdname);
E
Eric Blake 已提交
16480
        return false;
16481
    } else {
E
Eric Blake 已提交
16482 16483
        /* Don't translate desc if it is "".  */
        const char *desc = vshCmddefGetInfo(def, "desc");
E
Eric Blake 已提交
16484
        const char *help = _(vshCmddefGetInfo(def, "help"));
16485
        char buf[256];
16486 16487
        uint32_t opts_need_arg;
        uint32_t opts_required;
16488
        bool shortopt = false; /* true if 'arg' works instead of '--opt arg' */
16489 16490 16491 16492

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

16496
        fputs(_("  NAME\n"), stdout);
16497 16498
        fprintf(stdout, "    %s - %s\n", def->name, help);

16499 16500 16501 16502 16503
        fputs(_("\n  SYNOPSIS\n"), stdout);
        fprintf(stdout, "    %s", def->name);
        if (def->opts) {
            const vshCmdOptDef *opt;
            for (opt = def->opts; opt->name; opt++) {
16504
                const char *fmt = "%s";
16505 16506
                switch (opt->type) {
                case VSH_OT_BOOL:
16507
                    fmt = "[--%s]";
16508 16509
                    break;
                case VSH_OT_INT:
E
Eric Blake 已提交
16510
                    /* xgettext:c-format */
E
Eric Blake 已提交
16511
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>"
16512
                           : _("[--%s <number>]"));
16513 16514
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
16515 16516
                    break;
                case VSH_OT_STRING:
E
Eric Blake 已提交
16517 16518
                    /* xgettext:c-format */
                    fmt = _("[--%s <string>]");
16519 16520
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
16521 16522
                    break;
                case VSH_OT_DATA:
E
Eric Blake 已提交
16523
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>" : "[<%s>]");
16524 16525
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
16526 16527 16528
                    break;
                case VSH_OT_ARGV:
                    /* xgettext:c-format */
16529 16530 16531 16532 16533 16534 16535 16536
                    if (shortopt) {
                        fmt = (opt->flags & VSH_OFLAG_REQ)
                            ? _("{[--%s] <string>}...")
                            : _("[[--%s] <string>]...");
                    } else {
                        fmt = (opt->flags & VSH_OFLAG_REQ) ? _("<%s>...")
                            : _("[<%s>]...");
                    }
16537 16538
                    break;
                default:
16539
                    assert(0);
16540
                }
16541
                fputc(' ', stdout);
E
Eric Blake 已提交
16542
                fprintf(stdout, fmt, opt->name);
16543
            }
K
Karel Zak 已提交
16544
        }
16545 16546 16547
        fputc('\n', stdout);

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

K
Karel Zak 已提交
16553
        if (def->opts) {
16554
            const vshCmdOptDef *opt;
16555
            fputs(_("\n  OPTIONS\n"), stdout);
16556
            for (opt = def->opts; opt->name; opt++) {
16557 16558
                switch (opt->type) {
                case VSH_OT_BOOL:
K
Karel Zak 已提交
16559
                    snprintf(buf, sizeof(buf), "--%s", opt->name);
16560 16561
                    break;
                case VSH_OT_INT:
16562
                    snprintf(buf, sizeof(buf),
E
Eric Blake 已提交
16563
                             (opt->flags & VSH_OFLAG_REQ) ? _("[--%s] <number>")
16564
                             : _("--%s <number>"), opt->name);
16565 16566
                    break;
                case VSH_OT_STRING:
16567
                    /* OT_STRING should never be VSH_OFLAG_REQ */
16568
                    snprintf(buf, sizeof(buf), _("--%s <string>"), opt->name);
16569 16570
                    break;
                case VSH_OT_DATA:
16571 16572
                    snprintf(buf, sizeof(buf), _("[--%s] <string>"),
                             opt->name);
16573 16574
                    break;
                case VSH_OT_ARGV:
16575 16576 16577
                    snprintf(buf, sizeof(buf),
                             shortopt ? _("[--%s] <string>") : _("<%s>"),
                             opt->name);
16578
                    break;
16579 16580 16581
                default:
                    assert(0);
                }
16582

E
Eric Blake 已提交
16583
                fprintf(stdout, "    %-15s  %s\n", buf, _(opt->help));
16584
            }
K
Karel Zak 已提交
16585 16586 16587
        }
        fputc('\n', stdout);
    }
E
Eric Blake 已提交
16588
    return true;
K
Karel Zak 已提交
16589 16590 16591 16592 16593 16594
}

/* ---------------
 * Utils for work with runtime commands data
 * ---------------
 */
16595 16596 16597
static void
vshCommandOptFree(vshCmdOpt * arg)
{
K
Karel Zak 已提交
16598 16599
    vshCmdOpt *a = arg;

16600
    while (a) {
K
Karel Zak 已提交
16601
        vshCmdOpt *tmp = a;
16602

K
Karel Zak 已提交
16603 16604
        a = a->next;

16605 16606
        VIR_FREE(tmp->data);
        VIR_FREE(tmp);
K
Karel Zak 已提交
16607 16608 16609 16610
    }
}

static void
16611
vshCommandFree(vshCmd *cmd)
16612
{
K
Karel Zak 已提交
16613 16614
    vshCmd *c = cmd;

16615
    while (c) {
K
Karel Zak 已提交
16616
        vshCmd *tmp = c;
16617

K
Karel Zak 已提交
16618 16619 16620 16621
        c = c->next;

        if (tmp->opts)
            vshCommandOptFree(tmp->opts);
16622
        VIR_FREE(tmp);
K
Karel Zak 已提交
16623 16624 16625
    }
}

E
Eric Blake 已提交
16626 16627 16628 16629 16630 16631 16632 16633 16634 16635 16636
/**
 * 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 已提交
16637
 */
E
Eric Blake 已提交
16638 16639
static int
vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
16640
{
E
Eric Blake 已提交
16641 16642
    vshCmdOpt *candidate = cmd->opts;
    const vshCmdOptDef *valid = cmd->def->opts;
16643

E
Eric Blake 已提交
16644 16645 16646 16647 16648 16649 16650
    /* 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 已提交
16651
    }
E
Eric Blake 已提交
16652 16653 16654 16655 16656 16657 16658

    /* Option not present, see if command requires it.  */
    *opt = NULL;
    while (valid) {
        if (!valid->name)
            break;
        if (STREQ(name, valid->name))
E
Eric Blake 已提交
16659
            return (valid->flags & VSH_OFLAG_REQ) == 0 ? 0 : -1;
E
Eric Blake 已提交
16660 16661 16662 16663
        valid++;
    }
    /* If we got here, the name is unknown.  */
    return -2;
K
Karel Zak 已提交
16664 16665
}

E
Eric Blake 已提交
16666 16667
/**
 * vshCommandOptInt:
16668 16669 16670 16671 16672 16673 16674
 * @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 已提交
16675
 * 0 if option not found and not required (@value untouched)
16676
 * <0 in all other cases (@value untouched)
K
Karel Zak 已提交
16677 16678
 */
static int
16679
vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
16680
{
E
Eric Blake 已提交
16681 16682 16683
    vshCmdOpt *arg;
    int ret;
    int num;
16684
    char *end_p = NULL;
16685

E
Eric Blake 已提交
16686 16687 16688 16689 16690 16691 16692
    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;
16693
    }
E
Eric Blake 已提交
16694 16695 16696 16697 16698 16699 16700

    num = strtol(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
K
Karel Zak 已提交
16701 16702
}

16703

E
Eric Blake 已提交
16704 16705 16706 16707 16708 16709
/**
 * vshCommandOptUInt:
 * @cmd command reference
 * @name option name
 * @value result
 *
16710 16711 16712 16713 16714 16715
 * Convert option to unsigned int
 * See vshCommandOptInt()
 */
static int
vshCommandOptUInt(const vshCmd *cmd, const char *name, unsigned int *value)
{
E
Eric Blake 已提交
16716 16717 16718
    vshCmdOpt *arg;
    int ret;
    unsigned int num;
16719 16720
    char *end_p = NULL;

E
Eric Blake 已提交
16721 16722 16723 16724 16725 16726 16727
    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;
16728
    }
E
Eric Blake 已提交
16729 16730 16731 16732 16733 16734 16735

    num = strtoul(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
16736 16737 16738
}


16739
/*
E
Eric Blake 已提交
16740 16741 16742 16743 16744
 * vshCommandOptUL:
 * @cmd command reference
 * @name option name
 * @value result
 *
16745 16746 16747 16748 16749
 * Convert option to unsigned long
 * See vshCommandOptInt()
 */
static int
vshCommandOptUL(const vshCmd *cmd, const char *name, unsigned long *value)
16750
{
E
Eric Blake 已提交
16751 16752
    vshCmdOpt *arg;
    int ret;
16753
    unsigned long num;
16754 16755
    char *end_p = NULL;

E
Eric Blake 已提交
16756 16757 16758 16759 16760 16761 16762
    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;
16763
    }
E
Eric Blake 已提交
16764 16765 16766 16767 16768 16769 16770

    num = strtoul(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
16771 16772
}

E
Eric Blake 已提交
16773 16774 16775 16776 16777 16778
/**
 * vshCommandOptString:
 * @cmd command reference
 * @name option name
 * @value result
 *
K
Karel Zak 已提交
16779
 * Returns option as STRING
E
Eric Blake 已提交
16780 16781 16782 16783
 * 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 已提交
16784
 */
16785 16786
static int
vshCommandOptString(const vshCmd *cmd, const char *name, const char **value)
16787
{
E
Eric Blake 已提交
16788 16789 16790 16791 16792 16793 16794 16795 16796 16797
    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;
16798
    }
16799

E
Eric Blake 已提交
16800
    if (!*arg->data && !(arg->def->flags & VSH_OFLAG_EMPTY_OK)) {
E
Eric Blake 已提交
16801 16802 16803 16804
        return -1;
    }
    *value = arg->data;
    return 1;
K
Karel Zak 已提交
16805 16806
}

E
Eric Blake 已提交
16807 16808 16809 16810 16811 16812
/**
 * vshCommandOptLongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
16813
 * Returns option as long long
16814
 * See vshCommandOptInt()
16815
 */
16816 16817 16818
static int
vshCommandOptLongLong(const vshCmd *cmd, const char *name,
                      long long *value)
16819
{
E
Eric Blake 已提交
16820 16821
    vshCmdOpt *arg;
    int ret;
16822
    long long num;
16823 16824
    char *end_p = NULL;

E
Eric Blake 已提交
16825 16826 16827 16828 16829 16830 16831
    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;
16832
    }
E
Eric Blake 已提交
16833 16834 16835 16836 16837 16838 16839

    num = strtoll(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
16840 16841
}

E
Eric Blake 已提交
16842 16843 16844 16845 16846 16847 16848 16849 16850
/**
 * vshCommandOptULongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
 * Returns option as long long
 * See vshCommandOptInt()
 */
16851 16852 16853 16854
static int
vshCommandOptULongLong(const vshCmd *cmd, const char *name,
                       unsigned long long *value)
{
E
Eric Blake 已提交
16855 16856
    vshCmdOpt *arg;
    int ret;
16857 16858 16859
    unsigned long long num;
    char *end_p = NULL;

E
Eric Blake 已提交
16860 16861 16862 16863 16864 16865 16866
    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;
16867
    }
E
Eric Blake 已提交
16868 16869 16870 16871 16872 16873 16874

    num = strtoull(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
16875 16876 16877
}


E
Eric Blake 已提交
16878 16879 16880 16881 16882 16883 16884 16885 16886
/**
 * 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 已提交
16887
 */
E
Eric Blake 已提交
16888
static bool
16889
vshCommandOptBool(const vshCmd *cmd, const char *name)
16890
{
E
Eric Blake 已提交
16891 16892 16893
    vshCmdOpt *dummy;

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

E
Eric Blake 已提交
16896 16897 16898 16899 16900
/**
 * vshCommandOptArgv:
 * @cmd command reference
 * @opt starting point for the search
 *
16901 16902
 * Returns the next argv argument after OPT (or the first one if OPT
 * is NULL), or NULL if no more are present.
16903
 *
16904
 * Requires that a VSH_OT_ARGV option be last in the
16905 16906
 * list of supported options in CMD->def->opts.
 */
16907 16908
static const vshCmdOpt *
vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt)
16909
{
16910
    opt = opt ? opt->next : cmd->opts;
16911 16912

    while (opt) {
E
Eric Blake 已提交
16913
        if (opt->def->type == VSH_OT_ARGV) {
16914
            return opt;
16915 16916 16917 16918 16919 16920
        }
        opt = opt->next;
    }
    return NULL;
}

J
Jim Meyering 已提交
16921 16922 16923 16924 16925 16926 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938
/* 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)
16939
        vshError(ctl, _("internal error: virsh %s: no %s VSH_OT_DATA option"),
J
Jim Meyering 已提交
16940 16941 16942
                 cmd->def->name, optname);
    return found;
}
16943

K
Karel Zak 已提交
16944
static virDomainPtr
J
Jim Meyering 已提交
16945
vshCommandOptDomainBy(vshControl *ctl, const vshCmd *cmd,
16946
                      const char **name, int flag)
16947
{
K
Karel Zak 已提交
16948
    virDomainPtr dom = NULL;
16949
    const char *n = NULL;
K
Karel Zak 已提交
16950
    int id;
J
Jim Meyering 已提交
16951 16952 16953
    const char *optname = "domain";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;
16954

16955
    if (vshCommandOptString(cmd, optname, &n) <= 0)
16956 16957
        return NULL;

16958
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
16959 16960
             cmd->def->name, optname, n);

K
Karel Zak 已提交
16961 16962
    if (name)
        *name = n;
16963

K
Karel Zak 已提交
16964
    /* try it by ID */
16965
    if (flag & VSH_BYID) {
16966
        if (virStrToLong_i(n, NULL, 10, &id) == 0 && id >= 0) {
16967 16968
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: <%s> seems like domain ID\n",
K
Karel Zak 已提交
16969 16970 16971
                     cmd->def->name, optname);
            dom = virDomainLookupByID(ctl->conn, id);
        }
16972
    }
K
Karel Zak 已提交
16973
    /* try it by UUID */
16974
    if (dom==NULL && (flag & VSH_BYUUID) && strlen(n)==VIR_UUID_STRING_BUFLEN-1) {
16975
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain UUID\n",
16976
                 cmd->def->name, optname);
K
Karel Zak 已提交
16977
        dom = virDomainLookupByUUIDString(ctl->conn, n);
K
Karel Zak 已提交
16978
    }
K
Karel Zak 已提交
16979
    /* try it by NAME */
16980
    if (dom==NULL && (flag & VSH_BYNAME)) {
16981
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain NAME\n",
16982
                 cmd->def->name, optname);
K
Karel Zak 已提交
16983
        dom = virDomainLookupByName(ctl->conn, n);
16984
    }
K
Karel Zak 已提交
16985

16986
    if (!dom)
16987
        vshError(ctl, _("failed to get domain '%s'"), n);
16988

K
Karel Zak 已提交
16989 16990 16991
    return dom;
}

16992
static virNetworkPtr
J
Jim Meyering 已提交
16993
vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
16994
                       const char **name, int flag)
16995 16996
{
    virNetworkPtr network = NULL;
16997
    const char *n = NULL;
J
Jim Meyering 已提交
16998 16999 17000
    const char *optname = "network";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;
17001

17002
    if (vshCommandOptString(cmd, optname, &n) <= 0)
17003 17004
        return NULL;

17005
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
17006 17007 17008 17009 17010 17011
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
17012
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
17013
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
17014
                 cmd->def->name, optname);
17015 17016 17017 17018
        network = virNetworkLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
    if (network==NULL && (flag & VSH_BYNAME)) {
17019
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network NAME\n",
17020 17021 17022 17023 17024
                 cmd->def->name, optname);
        network = virNetworkLookupByName(ctl->conn, n);
    }

    if (!network)
17025
        vshError(ctl, _("failed to get network '%s'"), n);
17026 17027 17028 17029

    return network;
}

17030 17031 17032

static virNWFilterPtr
vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd,
17033
                        const char **name, int flag)
17034 17035
{
    virNWFilterPtr nwfilter = NULL;
17036
    const char *n = NULL;
17037 17038 17039 17040
    const char *optname = "nwfilter";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;

17041
    if (vshCommandOptString(cmd, optname, &n) <= 0)
17042 17043
        return NULL;

17044
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
17045 17046 17047 17048 17049 17050 17051
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
17052
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as nwfilter UUID\n",
17053 17054 17055 17056 17057
                 cmd->def->name, optname);
        nwfilter = virNWFilterLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
    if (nwfilter == NULL && (flag & VSH_BYNAME)) {
17058
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as nwfilter NAME\n",
17059 17060 17061 17062 17063 17064 17065 17066 17067 17068
                 cmd->def->name, optname);
        nwfilter = virNWFilterLookupByName(ctl->conn, n);
    }

    if (!nwfilter)
        vshError(ctl, _("failed to get nwfilter '%s'"), n);

    return nwfilter;
}

17069 17070
static virInterfacePtr
vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd,
17071
                         const char *optname,
17072
                         const char **name, int flag)
17073 17074
{
    virInterfacePtr iface = NULL;
17075
    const char *n = NULL;
17076 17077 17078

    if (!optname)
       optname = "interface";
17079 17080 17081
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;

17082
    if (vshCommandOptString(cmd, optname, &n) <= 0)
17083 17084
        return NULL;

17085
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
17086 17087 17088 17089 17090 17091
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by NAME */
17092
    if ((flag & VSH_BYNAME)) {
17093
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface NAME\n",
17094 17095 17096 17097 17098
                 cmd->def->name, optname);
        iface = virInterfaceLookupByName(ctl->conn, n);
    }
    /* try it by MAC */
    if ((iface == NULL) && (flag & VSH_BYMAC)) {
17099
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface MAC\n",
17100 17101 17102 17103 17104
                 cmd->def->name, optname);
        iface = virInterfaceLookupByMACString(ctl->conn, n);
    }

    if (!iface)
17105
        vshError(ctl, _("failed to get interface '%s'"), n);
17106 17107 17108 17109

    return iface;
}

17110
static virStoragePoolPtr
17111
vshCommandOptPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname,
17112
                    const char **name, int flag)
17113 17114
{
    virStoragePoolPtr pool = NULL;
17115
    const char *n = NULL;
17116

17117
    if (vshCommandOptString(cmd, optname, &n) <= 0)
17118 17119
        return NULL;

17120
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
17121 17122 17123 17124 17125 17126
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
17127
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
17128
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool UUID\n",
17129
                 cmd->def->name, optname);
17130 17131 17132
        pool = virStoragePoolLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
17133
    if (pool == NULL && (flag & VSH_BYNAME)) {
17134
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool NAME\n",
17135 17136 17137 17138 17139
                 cmd->def->name, optname);
        pool = virStoragePoolLookupByName(ctl->conn, n);
    }

    if (!pool)
17140
        vshError(ctl, _("failed to get pool '%s'"), n);
17141 17142 17143 17144 17145

    return pool;
}

static virStorageVolPtr
17146
vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd,
17147 17148
                   const char *optname,
                   const char *pooloptname,
17149
                   const char **name, int flag)
17150 17151 17152
{
    virStorageVolPtr vol = NULL;
    virStoragePoolPtr pool = NULL;
17153
    const char *n = NULL, *p = NULL;
17154

17155
    if (vshCommandOptString(cmd, optname, &n) <= 0)
17156 17157
        return NULL;

17158
    if (pooloptname != NULL && vshCommandOptString(cmd, pooloptname, &p) < 0) {
17159
        vshError(ctl, "%s", _("missing option"));
17160
        return NULL;
17161
    }
17162 17163 17164 17165

    if (p)
        pool = vshCommandOptPoolBy(ctl, cmd, pooloptname, name, flag);

17166
    vshDebug(ctl, VSH_ERR_DEBUG, "%s: found option <%s>: %s\n",
17167 17168 17169 17170 17171
             cmd->def->name, optname, n);

    if (name)
        *name = n;

17172
    /* try it by name */
17173
    if (pool && (flag & VSH_BYNAME)) {
17174
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol name\n",
17175 17176 17177
                 cmd->def->name, optname);
        vol = virStorageVolLookupByName(pool, n);
    }
17178
    /* try it by key */
17179
    if (vol == NULL && (flag & VSH_BYUUID)) {
17180
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol key\n",
17181 17182 17183
                 cmd->def->name, optname);
        vol = virStorageVolLookupByKey(ctl->conn, n);
    }
17184
    /* try it by path */
17185
    if (vol == NULL && (flag & VSH_BYUUID)) {
17186
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol path\n",
17187 17188 17189 17190
                 cmd->def->name, optname);
        vol = virStorageVolLookupByPath(ctl->conn, n);
    }

17191 17192 17193 17194
    if (!vol) {
        if (pool)
            vshError(ctl, _("failed to get vol '%s'"), n);
        else
17195 17196
            vshError(ctl, _("failed to get vol '%s', specifying --%s "
                            "might help"), n, pooloptname);
17197
    }
17198 17199 17200 17201 17202 17203 17204

    if (pool)
        virStoragePoolFree(pool);

    return vol;
}

17205
static virSecretPtr
17206
vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, const char **name)
17207 17208
{
    virSecretPtr secret = NULL;
17209
    const char *n = NULL;
17210 17211 17212 17213 17214
    const char *optname = "secret";

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

17215
    if (vshCommandOptString(cmd, optname, &n) <= 0)
17216 17217
        return NULL;

17218 17219
    vshDebug(ctl, VSH_ERR_DEBUG,
             "%s: found option <%s>: %s\n", cmd->def->name, optname, n);
17220 17221 17222 17223 17224 17225 17226

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

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

    if (secret == NULL)
17227
        vshError(ctl, _("failed to get secret '%s'"), n);
17228 17229 17230 17231

    return secret;
}

K
Karel Zak 已提交
17232 17233 17234
/*
 * Executes command(s) and returns return code from last command
 */
E
Eric Blake 已提交
17235
static bool
17236
vshCommandRun(vshControl *ctl, const vshCmd *cmd)
17237
{
E
Eric Blake 已提交
17238
    bool ret = true;
17239 17240

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

17244 17245
        if ((ctl->conn == NULL || disconnected) &&
            !(cmd->def->flags & VSH_CMD_FLAG_NOCONNECT))
17246 17247
            vshReconnect(ctl);

17248
        if (enable_timing)
K
Karel Zak 已提交
17249
            GETTIMEOFDAY(&before);
17250

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

17253
        if (enable_timing)
K
Karel Zak 已提交
17254
            GETTIMEOFDAY(&after);
17255

17256 17257 17258 17259 17260 17261 17262 17263 17264 17265
        /* 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++;

17266
        if (!ret)
J
John Levon 已提交
17267 17268
            virshReportError(ctl);

17269
        if (!ret && disconnected != 0)
17270 17271
            vshReconnect(ctl);

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

17275
        if (enable_timing)
17276
            vshPrint(ctl, _("\n(Time: %.3f ms)\n\n"),
17277 17278
                     DIFF_MSEC(&after, &before));
        else
K
Karel Zak 已提交
17279
            vshPrintExtra(ctl, "\n");
K
Karel Zak 已提交
17280 17281 17282 17283 17284 17285
        cmd = cmd->next;
    }
    return ret;
}

/* ---------------
17286
 * Command parsing
K
Karel Zak 已提交
17287 17288 17289
 * ---------------
 */

17290 17291 17292 17293 17294 17295 17296 17297 17298 17299
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 已提交
17300
    /* vshCommandStringGetArg() */
17301
    char *pos;
L
Lai Jiangshan 已提交
17302 17303 17304
    /* vshCommandArgvGetArg() */
    char **arg_pos;
    char **arg_end;
17305 17306
} vshCommandParser;

E
Eric Blake 已提交
17307
static bool
17308
vshCommandParse(vshControl *ctl, vshCommandParser *parser)
17309
{
K
Karel Zak 已提交
17310 17311 17312
    char *tkdata = NULL;
    vshCmd *clast = NULL;
    vshCmdOpt *first = NULL;
17313

K
Karel Zak 已提交
17314 17315 17316 17317
    if (ctl->cmd) {
        vshCommandFree(ctl->cmd);
        ctl->cmd = NULL;
    }
17318

17319
    while (1) {
K
Karel Zak 已提交
17320
        vshCmdOpt *last = NULL;
17321
        const vshCmdDef *cmd = NULL;
17322
        vshCommandToken tk;
L
Lai Jiangshan 已提交
17323
        bool data_only = false;
17324 17325 17326
        uint32_t opts_need_arg = 0;
        uint32_t opts_required = 0;
        uint32_t opts_seen = 0;
17327

K
Karel Zak 已提交
17328
        first = NULL;
17329

17330
        while (1) {
17331
            const vshCmdOptDef *opt = NULL;
17332

K
Karel Zak 已提交
17333
            tkdata = NULL;
17334
            tk = parser->getNextArg(ctl, parser, &tkdata);
17335 17336

            if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
17337
                goto syntaxError;
H
Hu Tao 已提交
17338 17339
            if (tk != VSH_TK_ARG) {
                VIR_FREE(tkdata);
17340
                break;
H
Hu Tao 已提交
17341
            }
17342 17343

            if (cmd == NULL) {
K
Karel Zak 已提交
17344 17345
                /* first token must be command name */
                if (!(cmd = vshCmddefSearch(tkdata))) {
17346
                    vshError(ctl, _("unknown command: '%s'"), tkdata);
17347
                    goto syntaxError;   /* ... or ignore this command only? */
K
Karel Zak 已提交
17348
                }
17349 17350 17351 17352 17353 17354 17355
                if (vshCmddefOptParse(cmd, &opts_need_arg,
                                      &opts_required) < 0) {
                    vshError(ctl,
                             _("internal error: bad options in command: '%s'"),
                             tkdata);
                    goto syntaxError;
                }
17356
                VIR_FREE(tkdata);
L
Lai Jiangshan 已提交
17357 17358 17359 17360
            } else if (data_only) {
                goto get_data;
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       c_isalnum(tkdata[2])) {
17361
                char *optstr = strchr(tkdata + 2, '=');
17362 17363
                int opt_index;

17364 17365 17366 17367
                if (optstr) {
                    *optstr = '\0'; /* convert the '=' to '\0' */
                    optstr = vshStrdup(ctl, optstr + 1);
                }
17368
                if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2,
17369
                                               &opts_seen, &opt_index))) {
17370
                    VIR_FREE(optstr);
K
Karel Zak 已提交
17371 17372
                    goto syntaxError;
                }
17373
                VIR_FREE(tkdata);
K
Karel Zak 已提交
17374 17375 17376

                if (opt->type != VSH_OT_BOOL) {
                    /* option data */
17377 17378 17379
                    if (optstr)
                        tkdata = optstr;
                    else
17380
                        tk = parser->getNextArg(ctl, parser, &tkdata);
17381
                    if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
17382
                        goto syntaxError;
17383
                    if (tk != VSH_TK_ARG) {
17384
                        vshError(ctl,
17385
                                 _("expected syntax: --%s <%s>"),
17386 17387
                                 opt->name,
                                 opt->type ==
17388
                                 VSH_OT_INT ? _("number") : _("string"));
K
Karel Zak 已提交
17389 17390
                        goto syntaxError;
                    }
17391 17392
                    if (opt->type != VSH_OT_ARGV)
                        opts_need_arg &= ~(1 << opt_index);
17393 17394 17395 17396 17397 17398 17399 17400
                } else {
                    tkdata = NULL;
                    if (optstr) {
                        vshError(ctl, _("invalid '=' after option --%s"),
                                opt->name);
                        VIR_FREE(optstr);
                        goto syntaxError;
                    }
K
Karel Zak 已提交
17401
                }
L
Lai Jiangshan 已提交
17402 17403 17404 17405
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       tkdata[2] == '\0') {
                data_only = true;
                continue;
17406
            } else {
L
Lai Jiangshan 已提交
17407
get_data:
17408 17409
                if (!(opt = vshCmddefGetData(cmd, &opts_need_arg,
                                             &opts_seen))) {
17410
                    vshError(ctl, _("unexpected data '%s'"), tkdata);
K
Karel Zak 已提交
17411 17412 17413 17414 17415
                    goto syntaxError;
                }
            }
            if (opt) {
                /* save option */
17416
                vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt));
17417

K
Karel Zak 已提交
17418 17419 17420 17421
                arg->def = opt;
                arg->data = tkdata;
                arg->next = NULL;
                tkdata = NULL;
17422

K
Karel Zak 已提交
17423 17424 17425 17426 17427
                if (!first)
                    first = arg;
                if (last)
                    last->next = arg;
                last = arg;
17428

17429
                vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n",
17430 17431
                         cmd->name,
                         opt->name,
17432 17433
                         opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"),
                         opt->type != VSH_OT_BOOL ? arg->data : _("(none)"));
K
Karel Zak 已提交
17434 17435
            }
        }
17436

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

K
Karel Zak 已提交
17441 17442 17443 17444
            c->opts = first;
            c->def = cmd;
            c->next = NULL;

17445
            if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) {
17446
                VIR_FREE(c);
17447
                goto syntaxError;
17448
            }
17449

K
Karel Zak 已提交
17450 17451 17452 17453 17454 17455
            if (!ctl->cmd)
                ctl->cmd = c;
            if (clast)
                clast->next = c;
            clast = c;
        }
17456 17457 17458

        if (tk == VSH_TK_END)
            break;
K
Karel Zak 已提交
17459
    }
17460

E
Eric Blake 已提交
17461
    return true;
K
Karel Zak 已提交
17462

17463
 syntaxError:
17464
    if (ctl->cmd) {
K
Karel Zak 已提交
17465
        vshCommandFree(ctl->cmd);
17466 17467
        ctl->cmd = NULL;
    }
K
Karel Zak 已提交
17468 17469
    if (first)
        vshCommandOptFree(first);
17470
    VIR_FREE(tkdata);
E
Eric Blake 已提交
17471
    return false;
K
Karel Zak 已提交
17472 17473
}

17474 17475 17476 17477 17478 17479 17480 17481 17482 17483 17484 17485 17486 17487 17488 17489 17490 17491
/* --------------------
 * 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 已提交
17492 17493
static bool
vshCommandArgvParse(vshControl *ctl, int nargs, char **argv)
17494 17495 17496 17497
{
    vshCommandParser parser;

    if (nargs <= 0)
E
Eric Blake 已提交
17498
        return false;
17499 17500 17501 17502 17503 17504 17505 17506 17507 17508 17509 17510 17511 17512 17513 17514 17515 17516 17517 17518 17519 17520 17521 17522 17523 17524 17525 17526 17527 17528 17529 17530 17531 17532 17533 17534 17535 17536 17537 17538 17539 17540 17541 17542 17543 17544 17545 17546 17547 17548 17549 17550 17551 17552 17553 17554 17555 17556 17557 17558 17559 17560 17561 17562 17563 17564 17565 17566 17567 17568 17569 17570

    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 已提交
17571 17572
static bool
vshCommandStringParse(vshControl *ctl, char *cmdstr)
17573 17574 17575 17576
{
    vshCommandParser parser;

    if (cmdstr == NULL || *cmdstr == '\0')
E
Eric Blake 已提交
17577
        return false;
17578 17579 17580 17581 17582 17583

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

K
Karel Zak 已提交
17584
/* ---------------
17585
 * Misc utils
K
Karel Zak 已提交
17586 17587
 * ---------------
 */
17588 17589 17590 17591 17592 17593 17594 17595 17596 17597 17598 17599 17600 17601 17602 17603 17604 17605 17606 17607 17608 17609 17610 17611 17612 17613 17614 17615
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 已提交
17616
static const char *
17617 17618
vshDomainStateToString(int state)
{
17619 17620
    /* Can't use virDomainStateTypeToString, because we want to mark
     * strings for translation.  */
17621
    switch ((virDomainState) state) {
17622
    case VIR_DOMAIN_RUNNING:
17623
        return N_("running");
17624
    case VIR_DOMAIN_BLOCKED:
17625
        return N_("idle");
17626
    case VIR_DOMAIN_PAUSED:
17627
        return N_("paused");
17628
    case VIR_DOMAIN_SHUTDOWN:
17629
        return N_("in shutdown");
17630
    case VIR_DOMAIN_SHUTOFF:
17631
        return N_("shut off");
17632
    case VIR_DOMAIN_CRASHED:
17633
        return N_("crashed");
17634
    case VIR_DOMAIN_NOSTATE:
17635
    default:
17636
        ;/*FALLTHROUGH*/
K
Karel Zak 已提交
17637
    }
17638
    return N_("no state");  /* = dom0 state */
K
Karel Zak 已提交
17639 17640
}

17641 17642 17643 17644 17645 17646 17647
static const char *
vshDomainStateReasonToString(int state, int reason)
{
    switch ((virDomainState) state) {
    case VIR_DOMAIN_NOSTATE:
        switch ((virDomainNostateReason) reason) {
        case VIR_DOMAIN_NOSTATE_UNKNOWN:
17648
        case VIR_DOMAIN_NOSTATE_LAST:
17649 17650 17651 17652 17653 17654 17655 17656 17657 17658 17659 17660 17661 17662 17663 17664 17665 17666 17667 17668 17669
            ;
        }
        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");
        case VIR_DOMAIN_RUNNING_UNKNOWN:
17670
        case VIR_DOMAIN_RUNNING_LAST:
17671 17672 17673 17674 17675 17676 17677
            ;
        }
        break;

    case VIR_DOMAIN_BLOCKED:
        switch ((virDomainBlockedReason) reason) {
        case VIR_DOMAIN_BLOCKED_UNKNOWN:
17678
        case VIR_DOMAIN_BLOCKED_LAST:
17679 17680 17681 17682 17683 17684 17685 17686 17687 17688 17689 17690 17691 17692 17693 17694 17695 17696 17697 17698
            ;
        }
        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");
17699 17700
        case VIR_DOMAIN_PAUSED_SHUTTING_DOWN:
            return N_("shutting down");
17701
        case VIR_DOMAIN_PAUSED_UNKNOWN:
17702
        case VIR_DOMAIN_PAUSED_LAST:
17703 17704 17705 17706 17707 17708 17709 17710 17711
            ;
        }
        break;

    case VIR_DOMAIN_SHUTDOWN:
        switch ((virDomainShutdownReason) reason) {
        case VIR_DOMAIN_SHUTDOWN_USER:
            return N_("user");
        case VIR_DOMAIN_SHUTDOWN_UNKNOWN:
17712
        case VIR_DOMAIN_SHUTDOWN_LAST:
17713 17714 17715 17716 17717 17718 17719 17720 17721 17722 17723 17724 17725 17726 17727 17728 17729 17730 17731 17732 17733
            ;
        }
        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:
17734
        case VIR_DOMAIN_SHUTOFF_LAST:
17735 17736 17737 17738 17739 17740 17741
            ;
        }
        break;

    case VIR_DOMAIN_CRASHED:
        switch ((virDomainCrashedReason) reason) {
        case VIR_DOMAIN_CRASHED_UNKNOWN:
17742
        case VIR_DOMAIN_CRASHED_LAST:
17743 17744 17745
            ;
        }
        break;
17746

17747
    case VIR_DOMAIN_LAST:
17748
        ;
17749 17750 17751 17752 17753
    }

    return N_("unknown");
}

17754 17755
/* Return a non-NULL string representation of a typed parameter; exit
 * if we are out of memory.  */
17756 17757 17758 17759 17760 17761 17762 17763 17764 17765 17766 17767 17768 17769 17770 17771 17772 17773 17774 17775 17776 17777 17778 17779 17780 17781 17782 17783 17784 17785 17786
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;

17787 17788 17789 17790
    case VIR_TYPED_PARAM_STRING:
        str = vshStrdup(ctl, item->value.s);
        break;

17791
    default:
17792
        vshError(ctl, _("unimplemented parameter type %d"), item->type);
17793 17794
    }

17795
    if (ret < 0) {
17796
        vshError(ctl, "%s", _("Out of memory"));
17797 17798
        exit(EXIT_FAILURE);
    }
17799 17800 17801 17802 17803 17804 17805 17806 17807 17808 17809 17810 17811 17812 17813 17814 17815 17816 17817 17818 17819 17820 17821
    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;
}

17822 17823 17824 17825 17826 17827 17828 17829 17830 17831 17832 17833
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");
17834 17835
    default:
        ;
17836 17837 17838 17839 17840
    }

    return N_("unknown");
}

17841 17842 17843 17844
static const char *
vshDomainVcpuStateToString(int state)
{
    switch (state) {
17845
    case VIR_VCPU_OFFLINE:
17846
        return N_("offline");
17847
    case VIR_VCPU_BLOCKED:
17848
        return N_("idle");
17849
    case VIR_VCPU_RUNNING:
17850
        return N_("running");
17851
    default:
17852
        ;/*FALLTHROUGH*/
17853
    }
17854
    return N_("no state");
17855 17856
}

E
Eric Blake 已提交
17857
static bool
17858
vshConnectionUsability(vshControl *ctl, virConnectPtr conn)
17859
{
17860 17861
    /* TODO: use something like virConnectionState() to
     *       check usability of the connection
K
Karel Zak 已提交
17862 17863
     */
    if (!conn) {
17864
        vshError(ctl, "%s", _("no valid connection"));
E
Eric Blake 已提交
17865
        return false;
K
Karel Zak 已提交
17866
    }
E
Eric Blake 已提交
17867
    return true;
K
Karel Zak 已提交
17868 17869
}

K
Karel Zak 已提交
17870
static void
17871
vshDebug(vshControl *ctl, int level, const char *format, ...)
17872
{
K
Karel Zak 已提交
17873
    va_list ap;
17874
    char *str;
K
Karel Zak 已提交
17875

17876 17877 17878 17879 17880 17881 17882
    /* Aligning log levels to that of libvirt.
     * Traces with levels >=  user-specified-level
     * gets logged into file
     */
    if (level < ctl->debug)
        return;

17883
    va_start(ap, format);
17884
    vshOutputLogFile(ctl, level, format, ap);
17885 17886
    va_end(ap);

K
Karel Zak 已提交
17887
    va_start(ap, format);
17888 17889 17890 17891 17892
    if (virVasprintf(&str, format, ap) < 0) {
        /* Skip debug messages on low memory */
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
17893
    va_end(ap);
17894 17895
    fputs(str, stdout);
    VIR_FREE(str);
K
Karel Zak 已提交
17896 17897 17898
}

static void
17899
vshPrintExtra(vshControl *ctl, const char *format, ...)
17900
{
K
Karel Zak 已提交
17901
    va_list ap;
17902
    char *str;
17903

17904
    if (ctl && ctl->quiet)
K
Karel Zak 已提交
17905
        return;
17906

K
Karel Zak 已提交
17907
    va_start(ap, format);
17908 17909 17910 17911 17912
    if (virVasprintf(&str, format, ap) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
17913
    va_end(ap);
17914
    fputs(str, stdout);
17915
    VIR_FREE(str);
K
Karel Zak 已提交
17916 17917
}

K
Karel Zak 已提交
17918

K
Karel Zak 已提交
17919
static void
17920
vshError(vshControl *ctl, const char *format, ...)
17921
{
K
Karel Zak 已提交
17922
    va_list ap;
17923
    char *str;
17924

17925 17926 17927 17928 17929
    if (ctl != NULL) {
        va_start(ap, format);
        vshOutputLogFile(ctl, VSH_ERR_ERROR, format, ap);
        va_end(ap);
    }
17930

17931 17932 17933 17934
    /* 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);
17935
    fputs(_("error: "), stderr);
17936

K
Karel Zak 已提交
17937
    va_start(ap, format);
17938 17939 17940
    /* We can't recursively call vshError on an OOM situation, so ignore
       failure here. */
    ignore_value(virVasprintf(&str, format, ap));
K
Karel Zak 已提交
17941 17942
    va_end(ap);

17943
    fprintf(stderr, "%s\n", NULLSTR(str));
17944
    fflush(stderr);
17945
    VIR_FREE(str);
K
Karel Zak 已提交
17946 17947
}

17948

J
Jiri Denemark 已提交
17949 17950 17951 17952 17953
static void
vshEventLoop(void *opaque)
{
    vshControl *ctl = opaque;

17954 17955 17956 17957 17958 17959 17960 17961 17962 17963
    while (1) {
        bool quit;
        virMutexLock(&ctl->lock);
        quit = ctl->quit;
        virMutexUnlock(&ctl->lock);

        if (quit)
            break;

        if (virEventRunDefaultImpl() < 0)
J
Jiri Denemark 已提交
17964 17965 17966 17967 17968
            virshReportError(ctl);
    }
}


K
Karel Zak 已提交
17969
/*
17970
 * Initialize connection.
K
Karel Zak 已提交
17971
 */
E
Eric Blake 已提交
17972
static bool
17973
vshInit(vshControl *ctl)
17974
{
17975 17976
    char *debugEnv;

K
Karel Zak 已提交
17977
    if (ctl->conn)
E
Eric Blake 已提交
17978
        return false;
K
Karel Zak 已提交
17979

J
Jiri Denemark 已提交
17980
    if (ctl->debug == VSH_DEBUG_DEFAULT) {
17981 17982 17983
        /* log level not set from commandline, check env variable */
        debugEnv = getenv("VIRSH_DEBUG");
        if (debugEnv) {
J
Jiri Denemark 已提交
17984 17985 17986
            int debug;
            if (virStrToLong_i(debugEnv, NULL, 10, &debug) < 0 ||
                debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) {
17987 17988
                vshError(ctl, "%s",
                         _("VIRSH_DEBUG not set with a valid numeric value"));
J
Jiri Denemark 已提交
17989 17990
            } else {
                ctl->debug = debug;
17991 17992 17993 17994 17995 17996 17997 17998 17999 18000 18001 18002
            }
        }
    }

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

18003 18004
    vshOpenLogFile(ctl);

18005 18006
    /* set up the library error handler */
    virSetErrorFunc(NULL, virshErrorHandler);
18007

18008 18009 18010
    /* set up the signals handlers to catch disconnections */
    vshSetupSignals();

18011
    if (virEventRegisterDefaultImpl() < 0)
E
Eric Blake 已提交
18012
        return false;
18013

J
Jiri Denemark 已提交
18014 18015 18016 18017
    if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0)
        return false;
    ctl->eventLoopStarted = true;

18018 18019 18020 18021
    if (ctl->name) {
        ctl->conn = virConnectOpenAuth(ctl->name,
                                       virConnectAuthPtrDefault,
                                       ctl->readonly ? VIR_CONNECT_RO : 0);
18022

18023 18024 18025 18026 18027 18028 18029 18030 18031 18032 18033
        /* 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;
        }
18034
    }
K
Karel Zak 已提交
18035

E
Eric Blake 已提交
18036
    return true;
K
Karel Zak 已提交
18037 18038
}

18039 18040
#define LOGFILE_FLAGS (O_WRONLY | O_APPEND | O_CREAT | O_SYNC)

18041 18042 18043 18044 18045 18046 18047 18048 18049 18050 18051 18052 18053 18054 18055 18056 18057 18058 18059
/**
 * 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:
18060
                vshError(ctl, "%s",
J
Jim Meyering 已提交
18061
                         _("failed to get the log file information"));
18062
                exit(EXIT_FAILURE);
18063 18064 18065
        }
    } else {
        if (!S_ISREG(st.st_mode)) {
18066 18067
            vshError(ctl, "%s", _("the log path is not a file"));
            exit(EXIT_FAILURE);
18068 18069 18070 18071
        }
    }

    /* log file open */
18072
    if ((ctl->log_fd = open(ctl->logfile, LOGFILE_FLAGS, FILE_MODE)) < 0) {
18073
        vshError(ctl, "%s",
J
Jim Meyering 已提交
18074
                 _("failed to open the log file. check the log file path"));
18075
        exit(EXIT_FAILURE);
18076 18077 18078 18079 18080 18081 18082 18083 18084
    }
}

/**
 * vshOutputLogFile:
 *
 * Outputting an error to log file.
 */
static void
18085 18086
vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format,
                 va_list ap)
18087
{
18088 18089 18090
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *str;
    size_t len;
18091 18092 18093 18094 18095 18096 18097 18098 18099 18100 18101 18102 18103 18104
    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);
18105
    virBufferAsprintf(&buf, "[%d.%02d.%02d %02d:%02d:%02d %s %d] ",
18106 18107 18108 18109 18110 18111
                      (1900 + stTm->tm_year),
                      (1 + stTm->tm_mon),
                      stTm->tm_mday,
                      stTm->tm_hour,
                      stTm->tm_min,
                      stTm->tm_sec,
18112 18113
                      SIGN_NAME,
                      (int) getpid());
18114 18115 18116 18117 18118 18119 18120 18121 18122 18123 18124 18125 18126 18127 18128 18129 18130 18131 18132 18133
    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;
    }
18134 18135 18136
    virBufferAsprintf(&buf, "%s ", lvl);
    virBufferVasprintf(&buf, msg_format, ap);
    virBufferAddChar(&buf, '\n');
18137

18138 18139
    if (virBufferError(&buf))
        goto error;
18140

18141 18142 18143 18144 18145
    str = virBufferContentAndReset(&buf);
    len = strlen(str);
    if (len > 1 && str[len - 2] == '\n') {
        str[len - 1] = '\0';
        len--;
18146
    }
18147

18148 18149 18150 18151 18152 18153 18154 18155 18156 18157 18158
    /* 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);
18159 18160 18161 18162 18163 18164 18165 18166 18167 18168 18169
}

/**
 * vshCloseLogFile:
 *
 * Close log file.
 */
static void
vshCloseLogFile(vshControl *ctl)
{
    /* log file close */
18170 18171 18172
    if (VIR_CLOSE(ctl->log_fd) < 0) {
        vshError(ctl, _("%s: failed to write log file: %s"),
                 ctl->logfile ? ctl->logfile : "?", strerror (errno));
18173 18174 18175
    }

    if (ctl->logfile) {
18176
        VIR_FREE(ctl->logfile);
18177 18178 18179 18180
        ctl->logfile = NULL;
    }
}

18181
#ifdef USE_READLINE
18182

K
Karel Zak 已提交
18183 18184 18185 18186 18187
/* -----------------
 * Readline stuff
 * -----------------
 */

18188
/*
K
Karel Zak 已提交
18189 18190
 * Generator function for command completion.  STATE lets us
 * know whether to start from scratch; without any state
18191
 * (i.e. STATE == 0), then we start at the top of the list.
K
Karel Zak 已提交
18192 18193
 */
static char *
18194 18195
vshReadlineCommandGenerator(const char *text, int state)
{
18196
    static int grp_list_index, cmd_list_index, len;
K
Karel Zak 已提交
18197
    const char *name;
18198 18199
    const vshCmdGrp *grp;
    const vshCmdDef *cmds;
K
Karel Zak 已提交
18200 18201

    if (!state) {
18202 18203
        grp_list_index = 0;
        cmd_list_index = 0;
18204
        len = strlen(text);
K
Karel Zak 已提交
18205 18206
    }

18207 18208
    grp = cmdGroups;

K
Karel Zak 已提交
18209
    /* Return the next name which partially matches from the
18210
     * command list.
K
Karel Zak 已提交
18211
     */
18212 18213 18214 18215 18216 18217 18218 18219 18220 18221 18222 18223 18224 18225
    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 已提交
18226 18227 18228 18229 18230 18231 18232
    }

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

static char *
18233 18234
vshReadlineOptionsGenerator(const char *text, int state)
{
K
Karel Zak 已提交
18235
    static int list_index, len;
18236
    static const vshCmdDef *cmd = NULL;
K
Karel Zak 已提交
18237
    const char *name;
K
Karel Zak 已提交
18238 18239 18240 18241 18242 18243 18244 18245 18246

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

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

18247
        cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1);
18248
        memcpy(cmdname, rl_line_buffer, p - rl_line_buffer);
K
Karel Zak 已提交
18249 18250 18251

        cmd = vshCmddefSearch(cmdname);
        list_index = 0;
18252
        len = strlen(text);
18253
        VIR_FREE(cmdname);
K
Karel Zak 已提交
18254 18255 18256 18257
    }

    if (!cmd)
        return NULL;
18258

18259 18260 18261
    if (!cmd->opts)
        return NULL;

K
Karel Zak 已提交
18262
    while ((name = cmd->opts[list_index].name)) {
18263
        const vshCmdOptDef *opt = &cmd->opts[list_index];
K
Karel Zak 已提交
18264
        char *res;
18265

K
Karel Zak 已提交
18266
        list_index++;
18267

18268
        if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV)
K
Karel Zak 已提交
18269 18270
            /* ignore non --option */
            continue;
18271

K
Karel Zak 已提交
18272
        if (len > 2) {
18273
            if (STRNEQLEN(name, text + 2, len - 2))
K
Karel Zak 已提交
18274 18275
                continue;
        }
18276
        res = vshMalloc(NULL, strlen(name) + 3);
18277
        snprintf(res, strlen(name) + 3,  "--%s", name);
K
Karel Zak 已提交
18278 18279 18280 18281 18282 18283 18284 18285
        return res;
    }

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

static char **
18286 18287 18288
vshReadlineCompletion(const char *text, int start,
                      int end ATTRIBUTE_UNUSED)
{
K
Karel Zak 已提交
18289 18290
    char **matches = (char **) NULL;

18291
    if (start == 0)
K
Karel Zak 已提交
18292
        /* command name generator */
18293
        matches = rl_completion_matches(text, vshReadlineCommandGenerator);
K
Karel Zak 已提交
18294 18295
    else
        /* commands options */
18296
        matches = rl_completion_matches(text, vshReadlineOptionsGenerator);
K
Karel Zak 已提交
18297 18298 18299 18300
    return matches;
}


18301 18302
static int
vshReadlineInit(vshControl *ctl)
18303
{
18304 18305
    char *userdir = NULL;

K
Karel Zak 已提交
18306 18307 18308 18309 18310
    /* 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;
18311 18312 18313

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

    /* Prepare to read/write history from/to the ~/.virsh/history file */
18316
    userdir = virGetUserDirectory(getuid());
18317

18318 18319
    if (userdir == NULL) {
        vshError(ctl, "%s", _("Could not determine home directory"));
18320
        return -1;
18321
    }
18322 18323 18324

    if (virAsprintf(&ctl->historydir, "%s/.virsh", userdir) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
18325
        VIR_FREE(userdir);
18326 18327 18328 18329 18330
        return -1;
    }

    if (virAsprintf(&ctl->historyfile, "%s/history", ctl->historydir) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
18331
        VIR_FREE(userdir);
18332 18333 18334
        return -1;
    }

18335
    VIR_FREE(userdir);
18336 18337 18338 18339 18340 18341 18342 18343 18344 18345 18346 18347 18348 18349 18350 18351 18352 18353

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

18354 18355
    VIR_FREE(ctl->historydir);
    VIR_FREE(ctl->historyfile);
K
Karel Zak 已提交
18356 18357
}

18358 18359 18360 18361 18362 18363
static char *
vshReadline (vshControl *ctl ATTRIBUTE_UNUSED, const char *prompt)
{
    return readline (prompt);
}

18364
#else /* !USE_READLINE */
18365

18366 18367 18368 18369 18370 18371 18372
static int
vshReadlineInit (vshControl *ctl ATTRIBUTE_UNUSED)
{
    /* empty */
    return 0;
}

18373
static void
18374
vshReadlineDeinit (vshControl *ctl ATTRIBUTE_UNUSED)
18375 18376 18377 18378 18379 18380 18381 18382 18383 18384 18385 18386 18387 18388 18389 18390 18391 18392 18393 18394 18395 18396 18397
{
    /* 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);
}

18398
#endif /* !USE_READLINE */
18399

18400 18401 18402 18403 18404 18405
static void
vshDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED)
{
    /* nothing to be done here */
}

K
Karel Zak 已提交
18406
/*
J
Jim Meyering 已提交
18407
 * Deinitialize virsh
K
Karel Zak 已提交
18408
 */
E
Eric Blake 已提交
18409
static bool
18410
vshDeinit(vshControl *ctl)
18411
{
18412
    vshReadlineDeinit(ctl);
18413
    vshCloseLogFile(ctl);
18414
    VIR_FREE(ctl->name);
K
Karel Zak 已提交
18415
    if (ctl->conn) {
18416 18417 18418
        int ret;
        if ((ret = virConnectClose(ctl->conn)) != 0) {
            vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
K
Karel Zak 已提交
18419 18420
        }
    }
D
Daniel P. Berrange 已提交
18421 18422
    virResetLastError();

J
Jiri Denemark 已提交
18423
    if (ctl->eventLoopStarted) {
18424 18425 18426 18427
        int timer;

        virMutexLock(&ctl->lock);
        ctl->quit = true;
J
Jiri Denemark 已提交
18428
        /* HACK: Add a dummy timeout to break event loop */
18429 18430 18431 18432 18433
        timer = virEventAddTimeout(0, vshDeinitTimer, NULL, NULL);
        virMutexUnlock(&ctl->lock);

        virThreadJoin(&ctl->eventLoop);

J
Jiri Denemark 已提交
18434 18435 18436 18437 18438 18439
        if (timer != -1)
            virEventRemoveTimeout(timer);

        ctl->eventLoopStarted = false;
    }

18440 18441
    virMutexDestroy(&ctl->lock);

E
Eric Blake 已提交
18442
    return true;
K
Karel Zak 已提交
18443
}
18444

K
Karel Zak 已提交
18445 18446 18447 18448
/*
 * Print usage
 */
static void
18449
vshUsage(void)
18450
{
18451
    const vshCmdGrp *grp;
18452
    const vshCmdDef *cmd;
18453

L
Lai Jiangshan 已提交
18454 18455
    fprintf(stdout, _("\n%s [options]... [<command_string>]"
                      "\n%s [options]... <command> [args...]\n\n"
18456
                      "  options:\n"
18457
                      "    -c | --connect=URI      hypervisor connection URI\n"
18458
                      "    -r | --readonly         connect readonly\n"
18459
                      "    -d | --debug=NUM        debug level [0-4]\n"
18460 18461 18462
                      "    -h | --help             this help\n"
                      "    -q | --quiet            quiet mode\n"
                      "    -t | --timing           print timing information\n"
18463 18464 18465 18466 18467
                      "    -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"
18468
                      "  commands (non interactive mode):\n\n"), progname, progname);
18469

18470 18471
    for (grp = cmdGroups; grp->name; grp++) {
        fprintf(stdout, _(" %s (help keyword '%s')\n"), grp->name, grp->keyword);
18472

18473 18474 18475 18476 18477 18478 18479 18480 18481
        for (cmd = grp->commands; cmd->name; cmd++)
            fprintf(stdout,
                    "    %-30s %s\n", cmd->name, _(vshCmddefGetInfo(cmd, "help")));

        fprintf(stdout, "\n");
    }

    fprintf(stdout, "%s",
            _("\n  (specify help <group> for details about the commands in the group)\n"));
18482 18483 18484
    fprintf(stdout, "%s",
            _("\n  (specify help <command> for details about the command)\n\n"));
    return;
K
Karel Zak 已提交
18485 18486
}

18487 18488 18489 18490 18491 18492 18493 18494 18495 18496
/*
 * 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 已提交
18497 18498
    vshPrint(ctl, "%s", _("Compiled with support for:\n"));
    vshPrint(ctl, "%s", _(" Hypervisors:"));
18499 18500 18501 18502 18503 18504 18505 18506 18507 18508 18509 18510 18511 18512 18513 18514 18515 18516 18517 18518 18519 18520 18521 18522 18523 18524 18525 18526 18527 18528 18529 18530 18531 18532 18533
#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 已提交
18534
    vshPrint(ctl, "%s", _(" Networking:"));
18535 18536 18537 18538 18539 18540 18541 18542 18543 18544 18545 18546 18547 18548 18549 18550 18551 18552 18553 18554 18555 18556 18557 18558 18559 18560
#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 已提交
18561
    vshPrint(ctl, "%s", _(" Storage:"));
18562 18563 18564 18565 18566 18567 18568 18569 18570 18571 18572 18573 18574 18575 18576 18577 18578 18579 18580 18581 18582 18583 18584
#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 已提交
18585
    vshPrint(ctl, "%s", _(" Miscellaneous:"));
18586
#ifdef WITH_SECDRIVER_APPARMOR
18587 18588 18589 18590 18591 18592 18593 18594 18595 18596 18597 18598 18599 18600 18601 18602 18603 18604 18605 18606 18607 18608 18609
    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 已提交
18610 18611 18612 18613
/*
 * argv[]:  virsh [options] [command]
 *
 */
E
Eric Blake 已提交
18614
static bool
18615
vshParseArgv(vshControl *ctl, int argc, char **argv)
18616
{
18617
    bool help = false;
18618
    int arg, len;
K
Karel Zak 已提交
18619
    struct option opt[] = {
E
Eric Blake 已提交
18620 18621 18622 18623 18624 18625 18626 18627
        {"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'},
18628
        {"escape", required_argument, NULL, 'e'},
E
Eric Blake 已提交
18629
        {NULL, 0, NULL, 0}
18630 18631
    };

18632 18633 18634
    /* 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. */
18635
    while ((arg = getopt_long(argc, argv, "+d:hqtc:vVrl:e:", opt, NULL)) != -1) {
18636
        switch (arg) {
18637
        case 'd':
D
Daniel Veillard 已提交
18638
            if (virStrToLong_i(optarg, NULL, 10, &ctl->debug) < 0) {
L
Laine Stump 已提交
18639
                vshError(ctl, "%s", _("option -d takes a numeric argument"));
D
Daniel Veillard 已提交
18640 18641
                exit(EXIT_FAILURE);
            }
18642 18643
            break;
        case 'h':
18644
            help = true;
18645 18646
            break;
        case 'q':
E
Eric Blake 已提交
18647
            ctl->quiet = true;
18648 18649
            break;
        case 't':
E
Eric Blake 已提交
18650
            ctl->timing = true;
18651 18652 18653 18654
            break;
        case 'c':
            ctl->name = vshStrdup(ctl, optarg);
            break;
E
Eric Blake 已提交
18655 18656 18657 18658 18659 18660
        case 'v':
            if (STRNEQ_NULLABLE(optarg, "long")) {
                puts(VERSION);
                exit(EXIT_SUCCESS);
            }
            /* fall through */
18661 18662 18663
        case 'V':
            vshShowVersion(ctl);
            exit(EXIT_SUCCESS);
18664
        case 'r':
E
Eric Blake 已提交
18665
            ctl->readonly = true;
18666
            break;
18667 18668 18669
        case 'l':
            ctl->logfile = vshStrdup(ctl, optarg);
            break;
18670 18671 18672 18673 18674 18675 18676 18677 18678 18679 18680 18681
        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;
18682
        default:
18683 18684
            vshError(ctl, _("unsupported option '-%c'. See --help."), arg);
            exit(EXIT_FAILURE);
K
Karel Zak 已提交
18685 18686 18687 18688
        }
    }

    if (help) {
18689 18690
        if (optind < argc) {
            vshError(ctl, _("extra argument '%s'. See --help."), argv[optind]);
18691 18692
            exit(EXIT_FAILURE);
        }
18693 18694 18695

        /* list all command */
        vshUsage();
K
Karel Zak 已提交
18696
        exit(EXIT_SUCCESS);
18697 18698
    }

18699
    if (argc > optind) {
K
Karel Zak 已提交
18700
        /* parse command */
E
Eric Blake 已提交
18701
        ctl->imode = false;
18702
        if (argc - optind == 1) {
18703
            vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]);
18704
            return vshCommandStringParse(ctl, argv[optind]);
L
Lai Jiangshan 已提交
18705
        } else {
18706
            return vshCommandArgvParse(ctl, argc - optind, argv + optind);
K
Karel Zak 已提交
18707 18708
        }
    }
E
Eric Blake 已提交
18709
    return true;
K
Karel Zak 已提交
18710 18711
}

18712 18713 18714 18715
int
main(int argc, char **argv)
{
    vshControl _ctl, *ctl = &_ctl;
18716
    char *defaultConn;
E
Eric Blake 已提交
18717
    bool ret = true;
K
Karel Zak 已提交
18718

18719 18720 18721
    memset(ctl, 0, sizeof(vshControl));
    ctl->imode = true;          /* default is interactive mode */
    ctl->log_fd = -1;           /* Initialize log file descriptor */
J
Jiri Denemark 已提交
18722
    ctl->debug = VSH_DEBUG_DEFAULT;
18723 18724
    ctl->escapeChar = CTRL_CLOSE_BRACKET;

18725

18726 18727
    if (!setlocale(LC_ALL, "")) {
        perror("setlocale");
18728
        /* failure to setup locale is not fatal */
18729
    }
18730
    if (!bindtextdomain(PACKAGE, LOCALEDIR)) {
18731
        perror("bindtextdomain");
E
Eric Blake 已提交
18732
        return EXIT_FAILURE;
18733
    }
18734
    if (!textdomain(PACKAGE)) {
18735
        perror("textdomain");
E
Eric Blake 已提交
18736
        return EXIT_FAILURE;
18737 18738
    }

18739 18740 18741 18742 18743
    if (virMutexInit(&ctl->lock) < 0) {
        vshError(ctl, "%s", _("Failed to initialize mutex"));
        return EXIT_FAILURE;
    }

18744 18745 18746 18747 18748
    if (virInitialize() < 0) {
        vshError(ctl, "%s", _("Failed to initialize libvirt"));
        return EXIT_FAILURE;
    }

18749
    if (!(progname = strrchr(argv[0], '/')))
K
Karel Zak 已提交
18750 18751 18752
        progname = argv[0];
    else
        progname++;
18753

18754
    if ((defaultConn = getenv("VIRSH_DEFAULT_CONNECT_URI"))) {
E
Eric Blake 已提交
18755
        ctl->name = vshStrdup(ctl, defaultConn);
18756 18757
    }

D
Daniel P. Berrange 已提交
18758 18759
    if (!vshParseArgv(ctl, argc, argv)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
18760
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
18761
    }
18762

D
Daniel P. Berrange 已提交
18763 18764
    if (!vshInit(ctl)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
18765
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
18766
    }
18767

K
Karel Zak 已提交
18768
    if (!ctl->imode) {
18769
        ret = vshCommandRun(ctl, ctl->cmd);
18770
    } else {
K
Karel Zak 已提交
18771 18772
        /* interactive mode */
        if (!ctl->quiet) {
K
Karel Zak 已提交
18773
            vshPrint(ctl,
18774
                     _("Welcome to %s, the virtualization interactive terminal.\n\n"),
18775
                     progname);
J
Jim Meyering 已提交
18776
            vshPrint(ctl, "%s",
18777
                     _("Type:  'help' for help with commands\n"
18778
                       "       'quit' to quit\n\n"));
K
Karel Zak 已提交
18779
        }
18780 18781 18782 18783 18784 18785

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

K
Karel Zak 已提交
18786
        do {
18787
            const char *prompt = ctl->readonly ? VSH_PROMPT_RO : VSH_PROMPT_RW;
18788
            ctl->cmdstr =
18789
                vshReadline(ctl, prompt);
18790 18791
            if (ctl->cmdstr == NULL)
                break;          /* EOF */
K
Karel Zak 已提交
18792
            if (*ctl->cmdstr) {
18793
#if USE_READLINE
K
Karel Zak 已提交
18794
                add_history(ctl->cmdstr);
18795
#endif
18796
                if (vshCommandStringParse(ctl, ctl->cmdstr))
K
Karel Zak 已提交
18797 18798
                    vshCommandRun(ctl, ctl->cmd);
            }
18799
            VIR_FREE(ctl->cmdstr);
18800
        } while (ctl->imode);
K
Karel Zak 已提交
18801

18802 18803
        if (ctl->cmdstr == NULL)
            fputc('\n', stdout);        /* line break after alone prompt */
K
Karel Zak 已提交
18804
    }
18805

K
Karel Zak 已提交
18806 18807
    vshDeinit(ctl);
    exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
18808
}