virsh.c 479.6 KB
Newer Older
1
/*
2
 * virsh.c: a shell to exercise the libvirt API
3
 *
4
 * Copyright (C) 2005, 2007-2011 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"
K
Karel Zak 已提交
63 64 65

static char *progname;

66 67
#define VIRSH_MAX_XML_FILE 10*1024*1024

K
Karel Zak 已提交
68 69 70
#define VSH_PROMPT_RW    "virsh # "
#define VSH_PROMPT_RO    "virsh > "

71 72
#define VIR_FROM_THIS VIR_FROM_NONE

K
Karel Zak 已提交
73 74 75 76 77
#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)

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

J
Jiri Denemark 已提交
105 106
#define VSH_DEBUG_DEFAULT VSH_ERR_ERROR

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

/*
128
 * vshCmdOptType - command option type
129
 */
K
Karel Zak 已提交
130
typedef enum {
131 132 133
    VSH_OT_BOOL,     /* optional boolean option */
    VSH_OT_STRING,   /* optional string option */
    VSH_OT_INT,      /* optional or mandatory int option */
134
    VSH_OT_DATA,     /* string data (as non-option) */
135
    VSH_OT_ARGV      /* remaining arguments */
K
Karel Zak 已提交
136 137
} vshCmdOptType;

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

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

/*
E
Eric Blake 已提交
169 170 171 172 173
 * 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 已提交
174
 */
175
typedef struct {
E
Eric Blake 已提交
176 177
    const char *name;           /* name of information, or NULL for list end */
    const char *data;           /* non-NULL information */
K
Karel Zak 已提交
178 179 180 181 182
} vshCmdInfo;

/*
 * vshCmdOptDef - command option definition
 */
183
typedef struct {
E
Eric Blake 已提交
184
    const char *name;           /* the name of option, or NULL for list end */
185
    vshCmdOptType type;         /* option type */
E
Eric Blake 已提交
186
    unsigned int flags;         /* flags */
E
Eric Blake 已提交
187
    const char *help;           /* non-NULL help string */
K
Karel Zak 已提交
188 189 190 191
} vshCmdOptDef;

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

202 203 204 205 206 207 208
/*
 * Command Usage Flags
 */
enum {
    VSH_CMD_FLAG_NOCONNECT = (1 << 0),  /* no prior connection needed */
};

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

/*
 * vshCmd - parsed command
 */
typedef struct __vshCmd {
224
    const vshCmdDef *def;       /* command definition */
225 226
    vshCmdOpt *opts;            /* list of command arguments */
    struct __vshCmd *next;      /* next command */
K
Karel Zak 已提交
227 228 229 230 231 232
} __vshCmd;

/*
 * vshControl
 */
typedef struct __vshControl {
K
Karel Zak 已提交
233
    char *name;                 /* connection name */
234
    virConnectPtr conn;         /* connection to hypervisor (MAY BE NULL) */
235 236
    vshCmd *cmd;                /* the current command */
    char *cmdstr;               /* string with command */
E
Eric Blake 已提交
237 238
    bool imode;                 /* interactive mode? */
    bool quiet;                 /* quiet mode */
239
    int debug;                  /* print debug messages? */
E
Eric Blake 已提交
240 241
    bool timing;                /* print timing info? */
    bool readonly;              /* connect readonly (first time only, not
242 243
                                 * during explicit connect command)
                                 */
244 245
    char *logfile;              /* log file name */
    int log_fd;                 /* log file descriptor */
246 247
    char *historydir;           /* readline history directory name */
    char *historyfile;          /* readline history file name */
248 249
    bool useGetInfo;            /* must use virDomainGetInfo, since
                                   virDomainGetState is not supported */
250 251
    bool useSnapshotOld;        /* cannot use virDomainSnapshotGetParent or
                                   virDomainSnapshotNumChildren */
K
Karel Zak 已提交
252
} __vshControl;
253

254
typedef struct vshCmdGrp {
E
Eric Blake 已提交
255
    const char *name;    /* name of group, or NULL for list end */
256 257 258
    const char *keyword; /* help keyword */
    const vshCmdDef *commands;
} vshCmdGrp;
259

260
static const vshCmdGrp cmdGroups[];
K
Karel Zak 已提交
261

262 263
static void vshError(vshControl *ctl, const char *format, ...)
    ATTRIBUTE_FMT_PRINTF(2, 3);
E
Eric Blake 已提交
264 265
static bool vshInit(vshControl *ctl);
static bool vshDeinit(vshControl *ctl);
266
static void vshUsage(void);
267
static void vshOpenLogFile(vshControl *ctl);
268 269
static void vshOutputLogFile(vshControl *ctl, int log_level, const char *format, va_list ap)
    ATTRIBUTE_FMT_PRINTF(3, 0);
270
static void vshCloseLogFile(vshControl *ctl);
K
Karel Zak 已提交
271

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

274
static const char *vshCmddefGetInfo(const vshCmdDef *cmd, const char *info);
275
static const vshCmdDef *vshCmddefSearch(const char *cmdname);
E
Eric Blake 已提交
276
static bool vshCmddefHelp(vshControl *ctl, const char *name);
277
static const vshCmdGrp *vshCmdGrpSearch(const char *grpname);
E
Eric Blake 已提交
278
static bool vshCmdGrpHelp(vshControl *ctl, const char *name);
K
Karel Zak 已提交
279

E
Eric Blake 已提交
280 281 282
static int vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
    ATTRIBUTE_RETURN_CHECK;
283 284
static int vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
285 286 287
static int vshCommandOptUInt(const vshCmd *cmd, const char *name,
                             unsigned int *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
288 289 290 291 292 293 294 295 296
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;
297 298 299
static int vshCommandOptULongLong(const vshCmd *cmd, const char *name,
                                  unsigned long long *value)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
E
Eric Blake 已提交
300
static bool vshCommandOptBool(const vshCmd *cmd, const char *name);
301 302
static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd,
                                          const vshCmdOpt *opt);
K
Karel Zak 已提交
303

304 305 306
#define VSH_BYID     (1 << 1)
#define VSH_BYUUID   (1 << 2)
#define VSH_BYNAME   (1 << 3)
307
#define VSH_BYMAC    (1 << 4)
K
Karel Zak 已提交
308

309
static virDomainPtr vshCommandOptDomainBy(vshControl *ctl, const vshCmd *cmd,
310
                                          const char **name, int flag);
K
Karel Zak 已提交
311 312

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

316
static virNetworkPtr vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
317
                                            const char **name, int flag);
318 319

/* default is lookup by Name and UUID */
J
Jim Meyering 已提交
320 321
#define vshCommandOptNetwork(_ctl, _cmd, _name)                    \
    vshCommandOptNetworkBy(_ctl, _cmd, _name,                      \
322 323
                           VSH_BYUUID|VSH_BYNAME)

324
static virNWFilterPtr vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd,
325
                                                  const char **name, int flag);
326 327 328 329 330 331

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

332
static virInterfacePtr vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd,
333
                                                const char **name, int flag);
334 335 336 337 338 339

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

340
static virStoragePoolPtr vshCommandOptPoolBy(vshControl *ctl, const vshCmd *cmd,
341
                            const char *optname, const char **name, int flag);
342 343 344 345 346 347

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

348
static virStorageVolPtr vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd,
349 350
                                           const char *optname,
                                           const char *pooloptname,
351
                                           const char **name, int flag);
352 353

/* default is lookup by Name and UUID */
354
#define vshCommandOptVol(_ctl, _cmd, _optname, _pooloptname, _name)   \
355 356 357
    vshCommandOptVolBy(_ctl, _cmd, _optname, _pooloptname, _name,     \
                           VSH_BYUUID|VSH_BYNAME)

358
static virSecretPtr vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd,
359
                                        const char **name);
360

361
static void vshPrintExtra(vshControl *ctl, const char *format, ...)
362
    ATTRIBUTE_FMT_PRINTF(2, 3);
363
static void vshDebug(vshControl *ctl, int level, const char *format, ...)
364
    ATTRIBUTE_FMT_PRINTF(3, 4);
K
Karel Zak 已提交
365 366

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

369
static int vshDomainState(vshControl *ctl, virDomainPtr dom, int *reason);
K
Karel Zak 已提交
370
static const char *vshDomainStateToString(int state);
371
static const char *vshDomainStateReasonToString(int state, int reason);
372
static const char *vshDomainControlStateToString(int state);
373
static const char *vshDomainVcpuStateToString(int state);
E
Eric Blake 已提交
374
static bool vshConnectionUsability(vshControl *ctl, virConnectPtr conn);
375 376 377 378
static virTypedParameterPtr vshFindTypedParamByName(const char *name,
                                                    virTypedParameterPtr list,
                                                    int count);
static char *vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item);
K
Karel Zak 已提交
379

380 381 382 383
static char *editWriteToTempFile (vshControl *ctl, const char *doc);
static int   editFile (vshControl *ctl, const char *filename);
static char *editReadBackFile (vshControl *ctl, const char *filename);

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

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

390
static char *_vshStrdup(vshControl *ctl, const char *s, const char *filename, int line);
391 392
#define vshStrdup(_ctl, _s)    _vshStrdup(_ctl, _s, __FILE__, __LINE__)

E
Eric Blake 已提交
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
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
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454

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;

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

459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
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 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
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)
{
506 507 508 509 510 511 512 513
    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)
514
            goto out;
515
    }
J
John Levon 已提交
516 517

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

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

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

529 530 531 532 533 534 535 536 537
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;
}

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

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

548 549 550 551 552 553
/*
 * 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
 */
554 555 556 557
static void vshCatchDisconnect(int sig, siginfo_t *siginfo,
                               void *context ATTRIBUTE_UNUSED) {
    if ((sig == SIGPIPE) ||
        (SA_SIGINFO && siginfo->si_signo == SIGPIPE))
558 559 560 561 562 563 564 565 566
        disconnected++;
}

/*
 * vshSetupSignals:
 *
 * Catch SIGPIPE signals which may arise when disconnection
 * from libvirtd occurs
 */
L
Laine Stump 已提交
567
static void
568 569 570 571 572 573 574 575 576 577 578 579 580
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 已提交
581
 * Reconnect after a disconnect from libvirtd
582 583
 *
 */
L
Laine Stump 已提交
584
static void
585 586 587 588 589 590
vshReconnect(vshControl *ctl)
{
    bool connected = false;

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

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

K
Karel Zak 已提交
606 607 608 609 610 611
/* ---------------
 * Commands
 * ---------------
 */

/*
612
 * "help" command
K
Karel Zak 已提交
613
 */
614
static const vshCmdInfo info_help[] = {
615
    {"help", N_("print help")},
616 617
    {"desc", N_("Prints global help, command specific help, or help for a\n"
                "    group of related commands")},
618

619
    {NULL, NULL}
K
Karel Zak 已提交
620 621
};

622
static const vshCmdOptDef opts_help[] = {
623
    {"command", VSH_OT_DATA, 0, N_("Prints global help, command specific help, or help for a group of related commands")},
624
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
625 626
};

E
Eric Blake 已提交
627
static bool
628
cmdHelp(vshControl *ctl, const vshCmd *cmd)
629
 {
630
    const char *name = NULL;
631

632
    if (vshCommandOptString(cmd, "command", &name) <= 0) {
633
        const vshCmdGrp *grp;
634
        const vshCmdDef *def;
635

636 637 638 639 640 641 642 643 644 645 646 647 648
        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 已提交
649
        return true;
650
    }
651

E
Eric Blake 已提交
652
    if (vshCmddefSearch(name)) {
653
        return vshCmddefHelp(ctl, name);
E
Eric Blake 已提交
654
    } else if (vshCmdGrpSearch(name)) {
655 656 657
        return vshCmdGrpHelp(ctl, name);
    } else {
        vshError(ctl, _("command or command group '%s' doesn't exist"), name);
E
Eric Blake 已提交
658
        return false;
K
Karel Zak 已提交
659 660 661
    }
}

662 663 664
/*
 * "autostart" command
 */
665
static const vshCmdInfo info_autostart[] = {
666
    {"help", N_("autostart a domain")},
667
    {"desc",
668
     N_("Configure a domain to be automatically started at boot.")},
669 670 671
    {NULL, NULL}
};

672
static const vshCmdOptDef opts_autostart[] = {
673 674
    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
675 676 677
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
678
static bool
679
cmdAutostart(vshControl *ctl, const vshCmd *cmd)
680 681
{
    virDomainPtr dom;
682
    const char *name;
683 684
    int autostart;

685
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
686
        return false;
687

J
Jim Meyering 已提交
688
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
689
        return false;
690 691 692 693

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

    if (virDomainSetAutostart(dom, autostart) < 0) {
694
        if (autostart)
695
            vshError(ctl, _("Failed to mark domain %s as autostarted"), name);
696
        else
697
            vshError(ctl, _("Failed to unmark domain %s as autostarted"), name);
698
        virDomainFree(dom);
E
Eric Blake 已提交
699
        return false;
700 701
    }

702
    if (autostart)
703
        vshPrint(ctl, _("Domain %s marked as autostarted\n"), name);
704
    else
705
        vshPrint(ctl, _("Domain %s unmarked as autostarted\n"), name);
706

707
    virDomainFree(dom);
E
Eric Blake 已提交
708
    return true;
709 710
}

K
Karel Zak 已提交
711
/*
712
 * "connect" command
K
Karel Zak 已提交
713
 */
714
static const vshCmdInfo info_connect[] = {
715
    {"help", N_("(re)connect to hypervisor")},
716
    {"desc",
717
     N_("Connect to local hypervisor. This is built-in command after shell start up.")},
718
    {NULL, NULL}
K
Karel Zak 已提交
719 720
};

721
static const vshCmdOptDef opts_connect[] = {
E
Eric Blake 已提交
722 723
    {"name",     VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
     N_("hypervisor connection URI")},
724
    {"readonly", VSH_OT_BOOL, 0, N_("read-only connection")},
725
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
726 727
};

E
Eric Blake 已提交
728
static bool
729
cmdConnect(vshControl *ctl, const vshCmd *cmd)
730
{
E
Eric Blake 已提交
731
    bool ro = vshCommandOptBool(cmd, "readonly");
732
    const char *name = NULL;
733

K
Karel Zak 已提交
734
    if (ctl->conn) {
735 736 737
        int ret;
        if ((ret = virConnectClose(ctl->conn)) != 0) {
            vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
E
Eric Blake 已提交
738
            return false;
K
Karel Zak 已提交
739 740 741
        }
        ctl->conn = NULL;
    }
742

743
    VIR_FREE(ctl->name);
744 745
    if (vshCommandOptString(cmd, "name", &name) < 0) {
        vshError(ctl, "%s", _("Please specify valid connection URI"));
E
Eric Blake 已提交
746
        return false;
747
    }
748
    ctl->name = vshStrdup(ctl, name);
K
Karel Zak 已提交
749

750
    ctl->useGetInfo = false;
751
    ctl->useSnapshotOld = false;
E
Eric Blake 已提交
752
    ctl->readonly = ro;
K
Karel Zak 已提交
753

754 755 756
    ctl->conn = virConnectOpenAuth(ctl->name, virConnectAuthPtrDefault,
                                   ctl->readonly ? VIR_CONNECT_RO : 0);

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

E
Eric Blake 已提交
760
    return !!ctl->conn;
K
Karel Zak 已提交
761 762
}

763 764
#ifndef WIN32

765
/*
766
 * "console" command
767
 */
768
static const vshCmdInfo info_console[] = {
769
    {"help", N_("connect to the guest console")},
770
    {"desc",
771
     N_("Connect the virtual serial console for the guest")},
772 773 774
    {NULL, NULL}
};

775
static const vshCmdOptDef opts_console[] = {
776
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
777
    {"devname", VSH_OT_STRING, 0, N_("character device name")},
778 779 780
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
781
static bool
782
cmdRunConsole(vshControl *ctl, virDomainPtr dom, const char *name)
783
{
E
Eric Blake 已提交
784
    bool ret = false;
785
    int state;
786

787
    if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
788 789 790 791
        vshError(ctl, "%s", _("Unable to get domain status"));
        goto cleanup;
    }

792
    if (state == VIR_DOMAIN_SHUTOFF) {
793 794 795 796
        vshError(ctl, "%s", _("The domain is not running"));
        goto cleanup;
    }

797 798
    vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
    vshPrintExtra(ctl, "%s", _("Escape character is ^]\n"));
799
    if (vshRunConsole(dom, name) == 0)
E
Eric Blake 已提交
800
        ret = true;
801 802

 cleanup:
803

804 805 806
    return ret;
}

E
Eric Blake 已提交
807
static bool
808 809 810
cmdConsole(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
811
    bool ret = false;
812
    const char *name = NULL;
813

814
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
815
        return false;
816 817

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

820 821 822 823
    if (vshCommandOptString(cmd, "devname", &name) < 0) {
        vshError(ctl, "%s", _("Invalid devname"));
        goto cleanup;
    }
824

825
    ret = cmdRunConsole(ctl, dom, name);
826

827
cleanup:
828 829 830 831
    virDomainFree(dom);
    return ret;
}

832 833 834
#endif /* WIN32 */


K
Karel Zak 已提交
835 836 837
/*
 * "list" command
 */
838
static const vshCmdInfo info_list[] = {
839 840
    {"help", N_("list domains")},
    {"desc", N_("Returns list of domains.")},
841
    {NULL, NULL}
K
Karel Zak 已提交
842 843
};

844
static const vshCmdOptDef opts_list[] = {
845 846
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive domains")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active domains")},
E
Eric Blake 已提交
847 848
    {"managed-save", VSH_OT_BOOL, 0,
     N_("mark domains with managed save state")},
849 850 851
    {NULL, 0, 0, NULL}
};

K
Karel Zak 已提交
852

E
Eric Blake 已提交
853
static bool
854
cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
855
{
856 857 858 859
    int inactive = vshCommandOptBool(cmd, "inactive");
    int all = vshCommandOptBool(cmd, "all");
    int active = !inactive || all ? 1 : 0;
    int *ids = NULL, maxid = 0, i;
860
    char **names = NULL;
861
    int maxname = 0;
E
Eric Blake 已提交
862 863 864
    bool managed = vshCommandOptBool(cmd, "managed-save");
    int state;

865
    inactive |= all;
K
Karel Zak 已提交
866

867
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
868
        return false;
869

870
    if (active) {
871 872
        maxid = virConnectNumOfDomains(ctl->conn);
        if (maxid < 0) {
873
            vshError(ctl, "%s", _("Failed to list active domains"));
E
Eric Blake 已提交
874
            return false;
875 876 877 878 879
        }
        if (maxid) {
            ids = vshMalloc(ctl, sizeof(int) * maxid);

            if ((maxid = virConnectListDomains(ctl->conn, &ids[0], maxid)) < 0) {
880
                vshError(ctl, "%s", _("Failed to list active domains"));
881
                VIR_FREE(ids);
E
Eric Blake 已提交
882
                return false;
883 884
            }

885
            qsort(&ids[0], maxid, sizeof(int), idsorter);
886
        }
887 888
    }
    if (inactive) {
889 890
        maxname = virConnectNumOfDefinedDomains(ctl->conn);
        if (maxname < 0) {
891
            vshError(ctl, "%s", _("Failed to list inactive domains"));
892
            VIR_FREE(ids);
E
Eric Blake 已提交
893
            return false;
894
        }
895 896 897 898
        if (maxname) {
            names = vshMalloc(ctl, sizeof(char *) * maxname);

            if ((maxname = virConnectListDefinedDomains(ctl->conn, names, maxname)) < 0) {
899
                vshError(ctl, "%s", _("Failed to list inactive domains"));
900 901
                VIR_FREE(ids);
                VIR_FREE(names);
E
Eric Blake 已提交
902
                return false;
903
            }
904

905
            qsort(&names[0], maxname, sizeof(char*), namesorter);
906
        }
907
    }
908
    vshPrintExtra(ctl, "%3s %-20s %s\n", _("Id"), _("Name"), _("State"));
K
Karel Zak 已提交
909
    vshPrintExtra(ctl, "----------------------------------\n");
910 911

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

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

K
Karel Zak 已提交
918
        vshPrint(ctl, "%3d %-20s %s\n",
919 920
                 virDomainGetID(dom),
                 virDomainGetName(dom),
E
Eric Blake 已提交
921
                 _(vshDomainStateToString(vshDomainState(ctl, dom, NULL))));
922
        virDomainFree(dom);
K
Karel Zak 已提交
923
    }
924 925 926 927
    for (i = 0; i < maxname; i++) {
        virDomainPtr dom = virDomainLookupByName(ctl->conn, names[i]);

        /* this kind of work with domains is not atomic operation */
928
        if (!dom) {
929
            VIR_FREE(names[i]);
930
            continue;
931
        }
932

E
Eric Blake 已提交
933 934 935 936 937
        state = vshDomainState(ctl, dom, NULL);
        if (managed && state == VIR_DOMAIN_SHUTOFF &&
            virDomainHasManagedSaveImage(dom, 0) > 0)
            state = -2;

938 939 940
        vshPrint(ctl, "%3s %-20s %s\n",
                 "-",
                 names[i],
E
Eric Blake 已提交
941
                 state == -2 ? _("saved") : _(vshDomainStateToString(state)));
942

943
        virDomainFree(dom);
944
        VIR_FREE(names[i]);
945
    }
946 947
    VIR_FREE(ids);
    VIR_FREE(names);
E
Eric Blake 已提交
948
    return true;
K
Karel Zak 已提交
949 950 951
}

/*
K
Karel Zak 已提交
952
 * "domstate" command
K
Karel Zak 已提交
953
 */
954
static const vshCmdInfo info_domstate[] = {
955 956
    {"help", N_("domain state")},
    {"desc", N_("Returns state about a domain.")},
957
    {NULL, NULL}
K
Karel Zak 已提交
958 959
};

960
static const vshCmdOptDef opts_domstate[] = {
961
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
962
    {"reason", VSH_OT_BOOL, 0, N_("also print reason for the state")},
963
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
964 965
};

E
Eric Blake 已提交
966
static bool
967
cmdDomstate(vshControl *ctl, const vshCmd *cmd)
968
{
K
Karel Zak 已提交
969
    virDomainPtr dom;
E
Eric Blake 已提交
970
    bool ret = true;
971 972
    int showReason = vshCommandOptBool(cmd, "reason");
    int state, reason;
973

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

J
Jim Meyering 已提交
977
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
978
        return false;
979

980
    if ((state = vshDomainState(ctl, dom, &reason)) < 0) {
E
Eric Blake 已提交
981
        ret = false;
982 983 984 985 986 987 988 989 990 991 992
        goto cleanup;
    }

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

994
cleanup:
995 996 997 998
    virDomainFree(dom);
    return ret;
}

999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
/*
 * "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;
}

1046 1047
/* "domblkstat" command
 */
1048
static const vshCmdInfo info_domblkstat[] = {
1049
    {"help", N_("get device block stats for a domain")},
1050 1051
    {"desc", N_("Get device block stats for a running domain. See man page or "
                "use --human for explanation of fields")},
1052 1053 1054
    {NULL,NULL}
};

1055
static const vshCmdOptDef opts_domblkstat[] = {
1056 1057
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
    {"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",
1072
      N_("number of read operations:") }, /* 0 */
1073
    { VIR_DOMAIN_BLOCK_STATS_READ_BYTES,        "rd_bytes",
1074
      N_("number of bytes read:") }, /* 1 */
1075
    { VIR_DOMAIN_BLOCK_STATS_WRITE_REQ,         "wr_req",
1076
      N_("number of write operations:") }, /* 2 */
1077
    { VIR_DOMAIN_BLOCK_STATS_WRITE_BYTES,       "wr_bytes",
1078
      N_("number of bytes written:") }, /* 3 */
1079
    { VIR_DOMAIN_BLOCK_STATS_ERRS,              "errs",
1080
      N_("error count:") }, /* 4 */
1081
    { VIR_DOMAIN_BLOCK_STATS_FLUSH_REQ,         NULL,
1082
      N_("number of flush operations:") }, /* 5 */
1083
    { VIR_DOMAIN_BLOCK_STATS_READ_TOTAL_TIMES,  NULL,
1084
      N_("total duration of reads (ns):") }, /* 6 */
1085
    { VIR_DOMAIN_BLOCK_STATS_WRITE_TOTAL_TIMES, NULL,
1086
      N_("total duration of writes (ns):") }, /* 7 */
1087 1088 1089 1090 1091 1092 1093
    { 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)                                     \
1094 1095
        vshPrint(ctl, "%s %-*s %lld\n", device,         \
                 human ? 31 : 0,                        \
1096 1097 1098
                 human ? _(domblkstat_output[ID].human) \
                 : domblkstat_output[ID].legacy,        \
                 VALUE);
1099

E
Eric Blake 已提交
1100
static bool
1101
cmdDomblkstat (vshControl *ctl, const vshCmd *cmd)
1102 1103
{
    virDomainPtr dom;
1104
    const char *name = NULL, *device = NULL;
1105
    struct _virDomainBlockStats stats;
1106
    virTypedParameterPtr params = NULL;
1107 1108 1109
    virTypedParameterPtr par = NULL;
    char *value = NULL;
    const char *field = NULL;
1110
    int rc, nparams = 0;
1111
    int i = 0;
1112
    bool ret = false;
1113
    bool human = vshCommandOptBool(cmd, "human"); /* human readable output */
1114

1115
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1116
        return false;
1117

1118
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
1119
        return false;
1120

1121
    if (vshCommandOptString(cmd, "device", &device) <= 0)
1122
        goto cleanup;
1123

1124 1125 1126 1127 1128 1129 1130
    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) {
1131 1132
        /* try older API if newer is not supported */
        if (last_error->code != VIR_ERR_NO_SUPPORT)
1133 1134
            goto cleanup;

1135 1136
        virFreeError(last_error);
        last_error = NULL;
1137

1138 1139 1140 1141 1142 1143
        if (virDomainBlockStats(dom, device, &stats,
                                sizeof stats) == -1) {
            vshError(ctl, _("Failed to get block stats %s %s"),
                     name, device);
            goto cleanup;
        }
1144

1145 1146 1147 1148
        /* human friendly output */
        if (human) {
            vshPrint(ctl, N_("Device: %s\n"), device);
            device = "";
1149
        }
1150 1151 1152 1153 1154 1155

        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);
1156
    } else {
1157
        params = vshCalloc(ctl, nparams, sizeof(*params));
1158

1159 1160 1161 1162
        if (virDomainBlockStatsFlags (dom, device, params, &nparams, 0) < 0) {
            vshError(ctl, _("Failed to get block stats %s %s"), name, device);
            goto cleanup;
        }
1163

1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
        /* 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;

            if (!(value = vshGetTypedParamValue(ctl, par)))
                continue;

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

1194 1195
            vshPrint(ctl, "%s %-*s %s\n", device,
                     human ? 31 : 0, field, value);
1196 1197 1198 1199 1200

            VIR_FREE(value);
        }

        /* go through the fields again, for remaining fields */
1201
        for (i = 0; i < nparams; i++) {
1202 1203
            if (!*params[i].field)
                continue;
1204

1205 1206 1207 1208 1209
            if (!(value = vshGetTypedParamValue(ctl, params+i)))
                continue;

            vshPrint(ctl, "%s %s %s\n", device, params[i].field, value);
            VIR_FREE(value);
1210 1211 1212 1213 1214 1215 1216
        }
    }

    ret = true;

cleanup:
    VIR_FREE(params);
1217
    virDomainFree(dom);
1218
    return ret;
1219
}
1220
#undef DOMBLKSTAT_LEGACY_PRINT
1221 1222 1223

/* "domifstat" command
 */
1224
static const vshCmdInfo info_domifstat[] = {
1225 1226
    {"help", N_("get network interface stats for a domain")},
    {"desc", N_("Get network interface stats for a running domain.")},
1227 1228 1229
    {NULL,NULL}
};

1230
static const vshCmdOptDef opts_domifstat[] = {
1231 1232
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device")},
1233 1234 1235
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1236
static bool
1237
cmdDomIfstat (vshControl *ctl, const vshCmd *cmd)
1238 1239
{
    virDomainPtr dom;
1240
    const char *name = NULL, *device = NULL;
1241 1242
    struct _virDomainInterfaceStats stats;

1243
    if (!vshConnectionUsability (ctl, ctl->conn))
E
Eric Blake 已提交
1244
        return false;
1245

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

1249
    if (vshCommandOptString (cmd, "interface", &device) <= 0) {
L
Laine Stump 已提交
1250
        virDomainFree(dom);
E
Eric Blake 已提交
1251
        return false;
L
Laine Stump 已提交
1252
    }
1253 1254

    if (virDomainInterfaceStats (dom, device, &stats, sizeof stats) == -1) {
1255
        vshError(ctl, _("Failed to get interface stats %s %s"), name, device);
1256
        virDomainFree(dom);
E
Eric Blake 已提交
1257
        return false;
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
    }

    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 已提交
1285
    return true;
1286 1287
}

1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
/* "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;
1308
    const char *iface;
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
    const char *state;
    const char *mac;
    const char *desc;
    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;

1328
    if (vshCommandOptString(cmd, "interface", &iface) <= 0)
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
        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 */
1357
    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
    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;
    }

    /* 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 &&
                xmlStrEqual(cur->name, BAD_CAST "mac")) {
                mac = virXMLPropString(cur, "address");

1380
                if (STRCASEEQ(mac, iface)) {
1381 1382 1383 1384 1385 1386 1387 1388 1389
                    VIR_FREE(mac);
                    goto hit;
                }
                VIR_FREE(mac);
            }
            cur = cur->next;
        }
    }

1390
    vshError(ctl, _("interface with address '%s' not found"), iface);
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 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
    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;
1472
    const char *iface = NULL;
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
    int flags = 0;
    char *state = NULL;
    char *mac = NULL;
    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;

1490
    if (vshCommandOptString (cmd, "interface", &iface) <= 0) {
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
        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;
    }

1504
    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527
    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;
    }

    /* 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 &&
                xmlStrEqual(cur->name, BAD_CAST "mac")) {

                mac = virXMLPropString(cur, "address");

1528
                if (STRCASEEQ(mac, iface)){
1529 1530 1531 1532 1533 1534 1535 1536
                    VIR_FREE(mac);
                    goto hit;
                }
            }
            cur = cur->next;
        }
    }

1537
    vshError(ctl, _("Interface with address '%s' not found."), iface);
1538 1539 1540 1541 1542 1543 1544 1545 1546
    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");
1547
            vshPrint(ctl, "%s %s", iface, state);
1548 1549 1550 1551 1552 1553 1554 1555
            VIR_FREE(state);

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

    /* attribute not found */
1556
    vshPrint(ctl, "%s default", iface);
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567

cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
    if (dom)
        virDomainFree(dom);

    return ret;
}

1568 1569 1570
/*
 * "dommemstats" command
 */
1571
static const vshCmdInfo info_dommemstat[] = {
1572 1573
    {"help", N_("get memory statistics for a domain")},
    {"desc", N_("Get memory statistics for a runnng domain.")},
1574 1575 1576
    {NULL,NULL}
};

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

E
Eric Blake 已提交
1582
static bool
1583
cmdDomMemStat(vshControl *ctl, const vshCmd *cmd)
1584 1585
{
    virDomainPtr dom;
1586
    const char *name;
1587 1588 1589
    struct _virDomainMemoryStat stats[VIR_DOMAIN_MEMORY_STAT_NR];
    unsigned int nr_stats, i;

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

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
1594
        return false;
1595 1596 1597 1598 1599

    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 已提交
1600
        return false;
1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615
    }

    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);
1616 1617
        if (stats[i].tag == VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON)
            vshPrint (ctl, "actual %llu\n", stats[i].val);
1618 1619 1620
    }

    virDomainFree(dom);
E
Eric Blake 已提交
1621
    return true;
1622 1623
}

1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
/*
 * "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 已提交
1639
static bool
1640 1641 1642 1643
cmdDomblkinfo(vshControl *ctl, const vshCmd *cmd)
{
    virDomainBlockInfo info;
    virDomainPtr dom;
E
Eric Blake 已提交
1644
    bool ret = true;
1645
    const char *device = NULL;
1646

1647
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1648
        return false;
1649 1650

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

1653
    if (vshCommandOptString (cmd, "device", &device) <= 0) {
1654
        virDomainFree(dom);
E
Eric Blake 已提交
1655
        return false;
1656 1657 1658 1659
    }

    if (virDomainGetBlockInfo(dom, device, &info, 0) < 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
1660
        return false;
1661 1662 1663 1664 1665 1666 1667 1668 1669 1670
    }

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

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
/*
 * "domblklist" command
 */
static const vshCmdInfo info_domblklist[] = {
    {"help", N_("list all domain blocks")},
    {"desc", N_("Get the names of block devices for a domain.")},
    {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")},
    {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;

    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;

1713
    xmldoc = virXMLParseStringCtxt(xml, _("(domain_definition)"), &ctxt);
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
    if (!xmldoc)
        goto cleanup;

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

    vshPrint(ctl, "%-10s %s\n", _("Target"), _("Source"));
    vshPrint(ctl, "------------------------------------------------\n");

    for (i = 0; i < ndisks; i++) {
        char *target;
        char *source;

        ctxt->node = disks[i];
        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);
        vshPrint(ctl, "%-10s %s\n", target, source ? source : "-");
        VIR_FREE(target);
        VIR_FREE(source);
    }

    ret = 0;

cleanup:
    VIR_FREE(disks);
    virDomainFree(dom);
    return ret;
}

1751 1752 1753
/*
 * "suspend" command
 */
1754
static const vshCmdInfo info_suspend[] = {
1755 1756
    {"help", N_("suspend a domain")},
    {"desc", N_("Suspend a running domain.")},
1757
    {NULL, NULL}
1758 1759
};

1760
static const vshCmdOptDef opts_suspend[] = {
1761
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
1762
    {NULL, 0, 0, NULL}
1763 1764
};

E
Eric Blake 已提交
1765
static bool
1766
cmdSuspend(vshControl *ctl, const vshCmd *cmd)
1767
{
1768
    virDomainPtr dom;
1769
    const char *name;
E
Eric Blake 已提交
1770
    bool ret = true;
1771

1772
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1773
        return false;
1774

J
Jim Meyering 已提交
1775
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
1776
        return false;
1777 1778

    if (virDomainSuspend(dom) == 0) {
1779
        vshPrint(ctl, _("Domain %s suspended\n"), name);
1780
    } else {
1781
        vshError(ctl, _("Failed to suspend domain %s"), name);
E
Eric Blake 已提交
1782
        ret = false;
1783
    }
1784

1785 1786 1787 1788
    virDomainFree(dom);
    return ret;
}

1789 1790 1791
/*
 * "create" command
 */
1792
static const vshCmdInfo info_create[] = {
1793 1794
    {"help", N_("create a domain from an XML file")},
    {"desc", N_("Create a domain.")},
1795 1796 1797
    {NULL, NULL}
};

1798
static const vshCmdOptDef opts_create[] = {
1799
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
1800
#ifndef WIN32
1801
    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
1802
#endif
1803
    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
1804
    {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroy the guest when virsh disconnects")},
1805 1806 1807
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1808
static bool
1809
cmdCreate(vshControl *ctl, const vshCmd *cmd)
1810 1811
{
    virDomainPtr dom;
1812
    const char *from = NULL;
E
Eric Blake 已提交
1813
    bool ret = true;
1814
    char *buffer;
1815
#ifndef WIN32
1816
    int console = vshCommandOptBool(cmd, "console");
1817
#endif
1818
    unsigned int flags = VIR_DOMAIN_NONE;
1819

1820
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1821
        return false;
1822

1823
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
1824
        return false;
1825

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

1829 1830
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_START_PAUSED;
1831 1832
    if (vshCommandOptBool(cmd, "autodestroy"))
        flags |= VIR_DOMAIN_START_AUTODESTROY;
1833 1834

    dom = virDomainCreateXML(ctl->conn, buffer, flags);
1835
    VIR_FREE(buffer);
1836

1837
    if (dom != NULL) {
1838
        vshPrint(ctl, _("Domain %s created from %s\n"),
1839
                 virDomainGetName(dom), from);
1840
#ifndef WIN32
1841
        if (console)
1842
            cmdRunConsole(ctl, dom, NULL);
1843
#endif
1844
        virDomainFree(dom);
1845
    } else {
1846
        vshError(ctl, _("Failed to create domain from %s"), from);
E
Eric Blake 已提交
1847
        ret = false;
1848 1849 1850 1851
    }
    return ret;
}

1852 1853 1854
/*
 * "define" command
 */
1855
static const vshCmdInfo info_define[] = {
1856 1857
    {"help", N_("define (but don't start) a domain from an XML file")},
    {"desc", N_("Define a domain.")},
1858 1859 1860
    {NULL, NULL}
};

1861
static const vshCmdOptDef opts_define[] = {
1862
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
1863 1864 1865
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1866
static bool
1867
cmdDefine(vshControl *ctl, const vshCmd *cmd)
1868 1869
{
    virDomainPtr dom;
1870
    const char *from = NULL;
E
Eric Blake 已提交
1871
    bool ret = true;
1872
    char *buffer;
1873

1874
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1875
        return false;
1876

1877
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
1878
        return false;
1879

1880
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
1881
        return false;
1882 1883

    dom = virDomainDefineXML(ctl->conn, buffer);
1884
    VIR_FREE(buffer);
1885

1886
    if (dom != NULL) {
1887
        vshPrint(ctl, _("Domain %s defined from %s\n"),
1888
                 virDomainGetName(dom), from);
1889
        virDomainFree(dom);
1890
    } else {
1891
        vshError(ctl, _("Failed to define domain from %s"), from);
E
Eric Blake 已提交
1892
        ret = false;
1893 1894 1895 1896 1897 1898 1899
    }
    return ret;
}

/*
 * "undefine" command
 */
1900
static const vshCmdInfo info_undefine[] = {
1901 1902 1903
    {"help", N_("undefine a domain")},
    {"desc",
     N_("Undefine an inactive domain, or convert persistent to transient.")},
1904 1905 1906
    {NULL, NULL}
};

1907
static const vshCmdOptDef opts_undefine[] = {
1908
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
1909
    {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
1910 1911
    {"snapshots-metadata", VSH_OT_BOOL, 0,
     N_("remove all domain snapshot metadata, if inactive")},
1912 1913 1914
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
1915
static bool
1916
cmdUndefine(vshControl *ctl, const vshCmd *cmd)
1917 1918
{
    virDomainPtr dom;
1919
    bool ret = false;
1920
    const char *name = NULL;
1921
    /* Flags to attempt.  */
E
Eric Blake 已提交
1922
    unsigned int flags = 0;
1923 1924 1925 1926
    /* User-requested actions.  */
    bool managed_save = vshCommandOptBool(cmd, "managed-save");
    bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
    /* Positive if these items exist.  */
1927
    int has_managed_save = 0;
1928 1929 1930 1931 1932
    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;
1933
    int rc = -1;
1934
    int running;
1935

1936
    if (managed_save) {
1937
        flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
1938 1939 1940 1941 1942 1943
        managed_save_safe = true;
    }
    if (snapshots_metadata) {
        flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
        snapshots_safe = true;
    }
1944

1945
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
1946
        return false;
1947

1948
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
1949
        return false;
1950

1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969
    /* 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;
            }
1970 1971
            virFreeError(last_error);
            last_error = NULL;
1972
            has_managed_save = 0;
1973 1974
        }

1975 1976
        has_snapshots = virDomainSnapshotNum(dom, 0);
        if (has_snapshots < 0) {
1977 1978
            if (last_error->code != VIR_ERR_NO_SUPPORT) {
                virshReportError(ctl);
1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990
                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.  */
1991 1992
                virFreeError(last_error);
                last_error = NULL;
1993 1994 1995 1996 1997
                has_snapshots_metadata = has_snapshots;
            } else {
                /* The server knew the new flag, all aspects of
                 * undefineFlags are safe.  */
                managed_save_safe = snapshots_safe = true;
1998
            }
1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
        }
    }
    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;
    }
2012

2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
    /* 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;
    }
2027

2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
    /* 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;
2040 2041 2042
        }
    }

2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056
    /* 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:
2057
    if (rc == 0) {
2058
        vshPrint(ctl, _("Domain %s has been undefined\n"), name);
2059
        ret = true;
2060
    } else {
2061
        vshError(ctl, _("Failed to undefine domain %s"), name);
2062 2063
    }

2064
cleanup:
2065
    virDomainFree(dom);
2066 2067 2068 2069 2070 2071 2072
    return ret;
}


/*
 * "start" command
 */
2073
static const vshCmdInfo info_start[] = {
2074
    {"help", N_("start a (previously defined) inactive domain")},
2075 2076 2077
    {"desc", N_("Start a domain, either from the last managedsave\n"
                "    state, or via a fresh boot if no managedsave state\n"
                "    is present.")},
2078 2079 2080
    {NULL, NULL}
};

2081
static const vshCmdOptDef opts_start[] = {
2082
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive domain")},
2083
#ifndef WIN32
2084
    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
2085
#endif
E
Eric Blake 已提交
2086
    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
2087 2088
    {"autodestroy", VSH_OT_BOOL, 0,
     N_("automatically destroy the guest when virsh disconnects")},
2089 2090
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when loading")},
2091 2092
    {"force-boot", VSH_OT_BOOL, 0,
     N_("force fresh boot by discarding any managed save")},
2093 2094 2095
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2096
static bool
2097
cmdStart(vshControl *ctl, const vshCmd *cmd)
2098 2099
{
    virDomainPtr dom;
2100
    bool ret = false;
2101
#ifndef WIN32
2102
    int console = vshCommandOptBool(cmd, "console");
2103
#endif
E
Eric Blake 已提交
2104
    unsigned int flags = VIR_DOMAIN_NONE;
2105
    int rc;
2106

2107
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2108
        return false;
2109

2110 2111
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
                                      VSH_BYNAME | VSH_BYUUID)))
E
Eric Blake 已提交
2112
        return false;
2113 2114

    if (virDomainGetID(dom) != (unsigned int)-1) {
2115
        vshError(ctl, "%s", _("Domain is already active"));
2116
        virDomainFree(dom);
E
Eric Blake 已提交
2117
        return false;
2118 2119
    }

E
Eric Blake 已提交
2120 2121
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_START_PAUSED;
2122 2123
    if (vshCommandOptBool(cmd, "autodestroy"))
        flags |= VIR_DOMAIN_START_AUTODESTROY;
2124 2125
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_START_BYPASS_CACHE;
2126 2127
    if (vshCommandOptBool(cmd, "force-boot"))
        flags |= VIR_DOMAIN_START_FORCE_BOOT;
E
Eric Blake 已提交
2128

2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153
    /* 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 已提交
2154 2155
    /* Prefer older API unless we have to pass a flag.  */
    if ((flags ? virDomainCreateWithFlags(dom, flags)
2156
         : virDomainCreate(dom)) < 0) {
2157
        vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
2158
        goto cleanup;
2159
    }
2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171

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

    ret = true;

cleanup:
2172
    virDomainFree(dom);
2173 2174 2175
    return ret;
}

2176 2177 2178
/*
 * "save" command
 */
2179
static const vshCmdInfo info_save[] = {
2180
    {"help", N_("save a domain state to a file")},
E
Eric Blake 已提交
2181
    {"desc", N_("Save the RAM state of a running domain.")},
2182
    {NULL, NULL}
2183 2184
};

2185
static const vshCmdOptDef opts_save[] = {
2186
    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
2187 2188
    {"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")},
2189 2190
    {"xml", VSH_OT_STRING, 0,
     N_("filename containing updated XML for the target")},
2191 2192
    {"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")},
2193
    {NULL, 0, 0, NULL}
2194 2195
};

E
Eric Blake 已提交
2196
static bool
2197
cmdSave(vshControl *ctl, const vshCmd *cmd)
2198
{
2199
    virDomainPtr dom;
2200 2201
    const char *name = NULL;
    const char *to = NULL;
2202
    bool ret = false;
E
Eric Blake 已提交
2203
    unsigned int flags = 0;
2204 2205
    const char *xmlfile = NULL;
    char *xml = NULL;
2206

2207
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2208
        return false;
2209

2210
    if (vshCommandOptString(cmd, "file", &to) <= 0)
E
Eric Blake 已提交
2211
        return false;
2212

2213 2214
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
2215 2216 2217 2218
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
2219

2220 2221 2222 2223 2224
    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
        vshError(ctl, "%s", _("malformed xml argument"));
        return false;
    }

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

2228 2229 2230 2231 2232 2233
    if (xmlfile &&
        virFileReadAll(xmlfile, 8192, &xml) < 0)
        goto cleanup;

    if (((flags || xml)
         ? virDomainSaveFlags(dom, to, xml, flags)
2234
         : virDomainSave(dom, to)) < 0) {
2235
        vshError(ctl, _("Failed to save domain %s to %s"), name, to);
2236
        goto cleanup;
2237
    }
2238

2239 2240 2241 2242
    vshPrint(ctl, _("Domain %s saved to %s\n"), name, to);
    ret = true;

cleanup:
2243
    VIR_FREE(xml);
2244 2245 2246 2247
    virDomainFree(dom);
    return ret;
}

2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268
/*
 * "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 已提交
2269
    unsigned int flags = 0;
2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305
    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")},
2306 2307
    {"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")},
2308 2309 2310 2311 2312 2313 2314 2315 2316 2317
    {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;
2318 2319 2320 2321 2322 2323
    unsigned int flags = 0;

    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338

    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;

2339
    if (virDomainSaveImageDefineXML(ctl->conn, file, xml, flags) < 0) {
2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362
        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")},
2363 2364
    {"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")},
2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375
    {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;
2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391
    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;
    }
2392 2393 2394 2395 2396 2397 2398 2399

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

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

    /* Get the XML configuration of the saved image.  */
2400
    doc = virDomainSaveImageGetXMLDesc(ctl->conn, file, getxml_flags);
2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417
    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;

2418 2419 2420
    /* 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) {
2421 2422 2423 2424 2425 2426 2427
        vshPrint(ctl, _("Saved image %s XML configuration not changed.\n"),
                 file);
        ret = true;
        goto cleanup;
    }

    /* Everything checks out, so redefine the xml.  */
2428 2429
    if (virDomainSaveImageDefineXML(ctl->conn, file, doc_edited,
                                    define_flags) < 0) {
2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446
        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;
}

2447 2448 2449 2450 2451
/*
 * "managedsave" command
 */
static const vshCmdInfo info_managedsave[] = {
    {"help", N_("managed save of a domain state")},
2452 2453 2454 2455
    {"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.")},
2456 2457 2458 2459
    {NULL, NULL}
};

static const vshCmdOptDef opts_managedsave[] = {
2460
    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
2461
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
2462 2463
    {"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")},
2464 2465 2466
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2467
static bool
2468 2469 2470
cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
2471
    const char *name;
2472
    bool ret = false;
E
Eric Blake 已提交
2473
    unsigned int flags = 0;
2474

2475
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2476
        return false;
2477

2478 2479
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
2480 2481 2482 2483
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
2484

2485
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2486
        return false;
2487

2488
    if (virDomainManagedSave(dom, flags) < 0) {
2489
        vshError(ctl, _("Failed to save domain %s state"), name);
2490
        goto cleanup;
2491 2492
    }

2493 2494 2495 2496
    vshPrint(ctl, _("Domain %s state saved by libvirt\n"), name);
    ret = true;

cleanup:
2497 2498 2499 2500
    virDomainFree(dom);
    return ret;
}

2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514
/*
 * "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 已提交
2515
static bool
2516 2517 2518
cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
2519
    const char *name;
E
Eric Blake 已提交
2520
    bool ret = false;
2521 2522
    int hassave;

2523
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2524
        return false;
2525 2526

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2527
        return false;
2528 2529 2530

    hassave = virDomainHasManagedSaveImage(dom, 0);
    if (hassave < 0) {
2531
        vshError(ctl, "%s", _("Failed to check for domain managed save image"));
2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547
        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 已提交
2548
    ret = true;
2549 2550 2551 2552 2553 2554

cleanup:
    virDomainFree(dom);
    return ret;
}

2555 2556 2557
/*
 * "schedinfo" command
 */
2558
static const vshCmdInfo info_schedinfo[] = {
2559 2560
    {"help", N_("show/set scheduler parameters")},
    {"desc", N_("Show/Set scheduler parameters.")},
2561 2562 2563
    {NULL, NULL}
};

2564
static const vshCmdOptDef opts_schedinfo[] = {
2565 2566 2567 2568
    {"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")},
2569 2570 2571
    {"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")},
2572 2573 2574
    {NULL, 0, 0, NULL}
};

W
Wen Congyang 已提交
2575
static int
2576
cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
2577
                   virTypedParameterPtr param)
2578
{
2579
    const char *data = NULL;
2580 2581 2582

    /* Legacy 'weight' parameter */
    if (STREQ(param->field, "weight") &&
2583
        param->type == VIR_TYPED_PARAM_UINT &&
2584 2585
        vshCommandOptBool(cmd, "weight")) {
        int val;
2586
        if (vshCommandOptInt(cmd, "weight", &val) <= 0) {
2587
            vshError(ctl, "%s", _("Invalid value of weight"));
2588
            return -1;
2589
        } else {
2590
            param->value.ui = val;
2591
        }
2592
        return 1;
2593 2594
    }

2595 2596
    /* Legacy 'cap' parameter */
    if (STREQ(param->field, "cap") &&
2597
        param->type == VIR_TYPED_PARAM_UINT &&
2598 2599
        vshCommandOptBool(cmd, "cap")) {
        int val;
2600
        if (vshCommandOptInt(cmd, "cap", &val) <= 0) {
2601
            vshError(ctl, "%s", _("Invalid value of cap"));
2602
            return -1;
2603
        } else {
2604
            param->value.ui = val;
2605
        }
2606
        return 1;
2607
    }
2608

2609
    if (vshCommandOptString(cmd, "set", &data) > 0) {
2610 2611 2612
        char *val = strchr(data, '=');
        int match = 0;
        if (!val) {
2613
            vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
2614
            return -1;
2615
        }
2616 2617 2618 2619 2620 2621 2622 2623 2624
        *val = '\0';
        match = STREQ(data, param->field);
        *val = '=';
        val++;

        if (!match)
            return 0;

        switch (param->type) {
2625
        case VIR_TYPED_PARAM_INT:
2626
            if (virStrToLong_i(val, NULL, 10, &param->value.i) < 0) {
2627
                vshError(ctl, "%s",
2628 2629 2630 2631
                         _("Invalid value for parameter, expecting an int"));
                return -1;
            }
            break;
2632
        case VIR_TYPED_PARAM_UINT:
2633
            if (virStrToLong_ui(val, NULL, 10, &param->value.ui) < 0) {
2634
                vshError(ctl, "%s",
2635 2636 2637 2638
                         _("Invalid value for parameter, expecting an unsigned int"));
                return -1;
            }
            break;
2639
        case VIR_TYPED_PARAM_LLONG:
2640
            if (virStrToLong_ll(val, NULL, 10, &param->value.l) < 0) {
2641
                vshError(ctl, "%s",
J
Jim Meyering 已提交
2642
                         _("Invalid value for parameter, expecting a long long"));
2643 2644 2645
                return -1;
            }
            break;
2646
        case VIR_TYPED_PARAM_ULLONG:
2647
            if (virStrToLong_ull(val, NULL, 10, &param->value.ul) < 0) {
2648
                vshError(ctl, "%s",
2649 2650 2651 2652
                         _("Invalid value for parameter, expecting an unsigned long long"));
                return -1;
            }
            break;
2653
        case VIR_TYPED_PARAM_DOUBLE:
2654
            if (virStrToDouble(val, NULL, &param->value.d) < 0) {
2655
                vshError(ctl, "%s", _("Invalid value for parameter, expecting a double"));
2656 2657 2658
                return -1;
            }
            break;
2659
        case VIR_TYPED_PARAM_BOOLEAN:
2660
            param->value.b = STREQ(val, "0") ? 0 : 1;
2661
        }
2662
        return 1;
2663
    }
2664

2665 2666
    return 0;
}
2667

2668

E
Eric Blake 已提交
2669
static bool
2670 2671 2672 2673
cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
{
    char *schedulertype;
    virDomainPtr dom;
2674
    virTypedParameterPtr params = NULL;
2675 2676 2677
    int nparams = 0;
    int update = 0;
    int i, ret;
W
Wen Congyang 已提交
2678
    bool ret_val = false;
2679 2680 2681 2682 2683 2684 2685 2686 2687 2688
    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;
        }
2689
        flags = VIR_DOMAIN_AFFECT_CURRENT;
2690 2691
    } else {
        if (config)
2692
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
2693
        if (live)
2694
            flags |= VIR_DOMAIN_AFFECT_LIVE;
2695
    }
2696

2697
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2698
        return false;
2699

2700
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
2701
        return false;
2702 2703 2704

    /* Print SchedulerType */
    schedulertype = virDomainGetSchedulerType(dom, &nparams);
2705
    if (schedulertype != NULL) {
2706
        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"),
2707
             schedulertype);
2708
        VIR_FREE(schedulertype);
2709
    } else {
2710
        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
2711
        goto cleanup;
2712 2713
    }

2714
    if (nparams) {
2715
        params = vshMalloc(ctl, sizeof(*params) * nparams);
2716

2717
        memset(params, 0, sizeof(*params) * nparams);
2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728
        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);
        }
2729 2730 2731 2732
        if (ret == -1)
            goto cleanup;

        /* See if any params are being set */
2733
        for (i = 0; i < nparams; i++) {
2734 2735 2736 2737 2738 2739 2740 2741 2742 2743
            ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
            if (ret == -1)
                goto cleanup;

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

        /* Update parameters & refresh data */
        if (update) {
2744 2745 2746 2747 2748
            if (flags || current)
                ret = virDomainSetSchedulerParametersFlags(dom, params,
                                                           nparams, flags);
            else
                ret = virDomainSetSchedulerParameters(dom, params, nparams);
2749 2750 2751
            if (ret == -1)
                goto cleanup;

2752 2753 2754 2755 2756 2757 2758
            if (flags || current)
                ret = virDomainGetSchedulerParametersFlags(dom, params,
                                                           &nparams,
                                                           ((live && config) ? 0
                                                            : flags));
            else
                ret = virDomainGetSchedulerParameters(dom, params, &nparams);
2759 2760
            if (ret == -1)
                goto cleanup;
2761 2762 2763 2764
        } 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.  */
2765 2766
            const char *var_value_pair = NULL;
            if (vshCommandOptString(cmd, "set", &var_value_pair) > 0) {
2767 2768 2769 2770
                vshError(ctl, _("invalid scheduler option: %s"),
                         var_value_pair);
                goto cleanup;
            }
2771 2772 2773 2774 2775 2776
            /* 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;
            }
2777 2778
        }

E
Eric Blake 已提交
2779
        ret_val = true;
2780
        for (i = 0; i < nparams; i++) {
2781
            switch (params[i].type) {
2782
            case VIR_TYPED_PARAM_INT:
2783
                 vshPrint(ctl, "%-15s: %d\n",  params[i].field, params[i].value.i);
2784
                 break;
2785
            case VIR_TYPED_PARAM_UINT:
2786
                 vshPrint(ctl, "%-15s: %u\n",  params[i].field, params[i].value.ui);
2787
                 break;
2788
            case VIR_TYPED_PARAM_LLONG:
2789
                 vshPrint(ctl, "%-15s: %lld\n",  params[i].field, params[i].value.l);
2790
                 break;
2791
            case VIR_TYPED_PARAM_ULLONG:
2792
                 vshPrint(ctl, "%-15s: %llu\n",  params[i].field, params[i].value.ul);
2793
                 break;
2794
            case VIR_TYPED_PARAM_DOUBLE:
2795
                 vshPrint(ctl, "%-15s: %f\n",  params[i].field, params[i].value.d);
2796
                 break;
2797
            case VIR_TYPED_PARAM_BOOLEAN:
2798
                 vshPrint(ctl, "%-15s: %d\n",  params[i].field, params[i].value.b);
2799 2800
                 break;
            default:
2801
                 vshPrint(ctl, "not implemented scheduler parameter type\n");
2802 2803 2804
            }
        }
    }
2805

2806
 cleanup:
2807
    VIR_FREE(params);
2808
    virDomainFree(dom);
2809
    return ret_val;
2810 2811
}

2812 2813 2814
/*
 * "restore" command
 */
2815
static const vshCmdInfo info_restore[] = {
2816 2817
    {"help", N_("restore a domain from a saved state in a file")},
    {"desc", N_("Restore a domain.")},
2818
    {NULL, NULL}
2819 2820
};

2821
static const vshCmdOptDef opts_restore[] = {
2822
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the state to restore")},
2823 2824
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when restoring")},
2825 2826
    {"xml", VSH_OT_STRING, 0,
     N_("filename containing updated XML for the target")},
2827 2828
    {"running", VSH_OT_BOOL, 0, N_("restore domain into running state")},
    {"paused", VSH_OT_BOOL, 0, N_("restore domain into paused state")},
2829
    {NULL, 0, 0, NULL}
2830 2831
};

E
Eric Blake 已提交
2832
static bool
2833
cmdRestore(vshControl *ctl, const vshCmd *cmd)
2834
{
2835
    const char *from = NULL;
2836
    bool ret = false;
E
Eric Blake 已提交
2837
    unsigned int flags = 0;
2838 2839
    const char *xmlfile = NULL;
    char *xml = NULL;
2840

2841
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2842
        return false;
2843

2844
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
2845
        return false;
2846

2847 2848
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
2849 2850 2851 2852
    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SAVE_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SAVE_PAUSED;
2853

2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864
    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)
2865
         : virDomainRestore(ctl->conn, from)) < 0) {
2866
        vshError(ctl, _("Failed to restore domain from %s"), from);
2867
        goto cleanup;
2868
    }
2869 2870 2871 2872 2873

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

cleanup:
2874
    VIR_FREE(xml);
2875 2876 2877
    return ret;
}

D
Daniel Veillard 已提交
2878 2879 2880
/*
 * "dump" command
 */
2881
static const vshCmdInfo info_dump[] = {
2882 2883
    {"help", N_("dump the core of a domain to a file for analysis")},
    {"desc", N_("Core dump a domain.")},
D
Daniel Veillard 已提交
2884 2885 2886
    {NULL, NULL}
};

2887
static const vshCmdOptDef opts_dump[] = {
2888 2889
    {"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")},
2890 2891
    {"bypass-cache", VSH_OT_BOOL, 0,
     N_("avoid file system cache when saving")},
2892
    {"reset", VSH_OT_BOOL, 0, N_("reset the domain after core dump")},
2893 2894
    {"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")},
D
Daniel Veillard 已提交
2895 2896 2897
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
2898
static bool
2899
cmdDump(vshControl *ctl, const vshCmd *cmd)
D
Daniel Veillard 已提交
2900 2901
{
    virDomainPtr dom;
2902 2903
    const char *name = NULL;
    const char *to = NULL;
2904
    bool ret = false;
E
Eric Blake 已提交
2905
    unsigned int flags = 0;
D
Daniel Veillard 已提交
2906

2907
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
2908
        return false;
D
Daniel Veillard 已提交
2909

2910
    if (vshCommandOptString(cmd, "file", &to) <= 0)
E
Eric Blake 已提交
2911
        return false;
D
Daniel Veillard 已提交
2912

J
Jim Meyering 已提交
2913
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
2914
        return false;
D
Daniel Veillard 已提交
2915

P
Paolo Bonzini 已提交
2916 2917
    if (vshCommandOptBool (cmd, "live"))
        flags |= VIR_DUMP_LIVE;
2918 2919
    if (vshCommandOptBool (cmd, "crash"))
        flags |= VIR_DUMP_CRASH;
2920 2921
    if (vshCommandOptBool(cmd, "bypass-cache"))
        flags |= VIR_DUMP_BYPASS_CACHE;
2922 2923
    if (vshCommandOptBool(cmd, "reset"))
        flags |= VIR_DUMP_RESET;
2924

2925
    if (virDomainCoreDump(dom, to, flags) < 0) {
2926
        vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
2927
        goto cleanup;
D
Daniel Veillard 已提交
2928 2929
    }

2930 2931 2932 2933
    vshPrint(ctl, _("Domain %s dumped to %s\n"), name, to);
    ret = true;

cleanup:
D
Daniel Veillard 已提交
2934 2935 2936 2937
    virDomainFree(dom);
    return ret;
}

2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011
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;
3012
    bool created = false;
3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051
    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;
        }
3052 3053
    } else {
        created = true;
3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085
    }

    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);
    return ret;
}

3086 3087 3088
/*
 * "resume" command
 */
3089
static const vshCmdInfo info_resume[] = {
3090 3091
    {"help", N_("resume a domain")},
    {"desc", N_("Resume a previously suspended domain.")},
3092
    {NULL, NULL}
3093 3094
};

3095
static const vshCmdOptDef opts_resume[] = {
3096
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3097
    {NULL, 0, 0, NULL}
3098 3099
};

E
Eric Blake 已提交
3100
static bool
3101
cmdResume(vshControl *ctl, const vshCmd *cmd)
3102
{
3103
    virDomainPtr dom;
E
Eric Blake 已提交
3104
    bool ret = true;
3105
    const char *name;
3106

3107
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3108
        return false;
3109

J
Jim Meyering 已提交
3110
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3111
        return false;
3112 3113

    if (virDomainResume(dom) == 0) {
3114
        vshPrint(ctl, _("Domain %s resumed\n"), name);
3115
    } else {
3116
        vshError(ctl, _("Failed to resume domain %s"), name);
E
Eric Blake 已提交
3117
        ret = false;
3118
    }
3119

3120 3121 3122 3123
    virDomainFree(dom);
    return ret;
}

3124 3125 3126
/*
 * "shutdown" command
 */
3127
static const vshCmdInfo info_shutdown[] = {
3128 3129
    {"help", N_("gracefully shutdown a domain")},
    {"desc", N_("Run shutdown in the target domain.")},
3130
    {NULL, NULL}
3131 3132
};

3133
static const vshCmdOptDef opts_shutdown[] = {
3134
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3135
    {NULL, 0, 0, NULL}
3136 3137
};

E
Eric Blake 已提交
3138
static bool
3139
cmdShutdown(vshControl *ctl, const vshCmd *cmd)
3140
{
3141
    virDomainPtr dom;
E
Eric Blake 已提交
3142
    bool ret = true;
3143
    const char *name;
3144

3145
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3146
        return false;
3147

J
Jim Meyering 已提交
3148
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3149
        return false;
3150 3151

    if (virDomainShutdown(dom) == 0) {
3152
        vshPrint(ctl, _("Domain %s is being shutdown\n"), name);
3153
    } else {
3154
        vshError(ctl, _("Failed to shutdown domain %s"), name);
E
Eric Blake 已提交
3155
        ret = false;
3156
    }
3157

3158 3159 3160 3161
    virDomainFree(dom);
    return ret;
}

3162 3163 3164
/*
 * "reboot" command
 */
3165
static const vshCmdInfo info_reboot[] = {
3166 3167
    {"help", N_("reboot a domain")},
    {"desc", N_("Run a reboot command in the target domain.")},
3168 3169 3170
    {NULL, NULL}
};

3171
static const vshCmdOptDef opts_reboot[] = {
3172
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3173 3174 3175
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3176
static bool
3177
cmdReboot(vshControl *ctl, const vshCmd *cmd)
3178 3179
{
    virDomainPtr dom;
E
Eric Blake 已提交
3180
    bool ret = true;
3181
    const char *name;
3182

3183
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3184
        return false;
3185

J
Jim Meyering 已提交
3186
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3187
        return false;
3188 3189

    if (virDomainReboot(dom, 0) == 0) {
3190
        vshPrint(ctl, _("Domain %s is being rebooted\n"), name);
3191
    } else {
3192
        vshError(ctl, _("Failed to reboot domain %s"), name);
E
Eric Blake 已提交
3193
        ret = false;
3194 3195 3196 3197 3198 3199
    }

    virDomainFree(dom);
    return ret;
}

X
Xu He Jie 已提交
3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237
/*
 * "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;
}

3238 3239 3240
/*
 * "destroy" command
 */
3241
static const vshCmdInfo info_destroy[] = {
3242 3243 3244
    {"help", N_("destroy (stop) a domain")},
    {"desc",
     N_("Forcefully stop a given domain, but leave its resources intact.")},
3245
    {NULL, NULL}
3246 3247
};

3248
static const vshCmdOptDef opts_destroy[] = {
3249
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3250
    {NULL, 0, 0, NULL}
3251 3252
};

E
Eric Blake 已提交
3253
static bool
3254
cmdDestroy(vshControl *ctl, const vshCmd *cmd)
3255
{
3256
    virDomainPtr dom;
E
Eric Blake 已提交
3257
    bool ret = true;
3258
    const char *name;
3259

3260
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3261
        return false;
3262

J
Jim Meyering 已提交
3263
    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
E
Eric Blake 已提交
3264
        return false;
3265 3266

    if (virDomainDestroy(dom) == 0) {
3267
        vshPrint(ctl, _("Domain %s destroyed\n"), name);
3268
    } else {
3269
        vshError(ctl, _("Failed to destroy domain %s"), name);
E
Eric Blake 已提交
3270
        ret = false;
3271
    }
3272

3273
    virDomainFree(dom);
K
Karel Zak 已提交
3274 3275 3276 3277
    return ret;
}

/*
3278
 * "dominfo" command
K
Karel Zak 已提交
3279
 */
3280
static const vshCmdInfo info_dominfo[] = {
3281 3282
    {"help", N_("domain information")},
    {"desc", N_("Returns basic information about the domain.")},
3283
    {NULL, NULL}
K
Karel Zak 已提交
3284 3285
};

3286
static const vshCmdOptDef opts_dominfo[] = {
3287
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3288
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
3289 3290
};

E
Eric Blake 已提交
3291
static bool
3292
cmdDominfo(vshControl *ctl, const vshCmd *cmd)
3293
{
K
Karel Zak 已提交
3294 3295
    virDomainInfo info;
    virDomainPtr dom;
3296
    virSecurityModel secmodel;
3297
    virSecurityLabelPtr seclabel;
3298
    int persistent = 0;
E
Eric Blake 已提交
3299 3300
    bool ret = true;
    int autostart;
3301
    unsigned int id;
3302
    char *str, uuid[VIR_UUID_STRING_BUFLEN];
3303
    int has_managed_save = 0;
3304

3305
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3306
        return false;
K
Karel Zak 已提交
3307

J
Jim Meyering 已提交
3308
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
3309
        return false;
3310

3311 3312
    id = virDomainGetID(dom);
    if (id == ((unsigned int)-1))
3313
        vshPrint(ctl, "%-15s %s\n", _("Id:"), "-");
3314
    else
3315
        vshPrint(ctl, "%-15s %d\n", _("Id:"), id);
3316 3317
    vshPrint(ctl, "%-15s %s\n", _("Name:"), virDomainGetName(dom));

K
Karel Zak 已提交
3318
    if (virDomainGetUUIDString(dom, &uuid[0])==0)
3319
        vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
3320 3321

    if ((str = virDomainGetOSType(dom))) {
3322
        vshPrint(ctl, "%-15s %s\n", _("OS Type:"), str);
3323
        VIR_FREE(str);
3324 3325 3326
    }

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

3330
        vshPrint(ctl, "%-15s %d\n", _("CPU(s):"), info.nrVirtCpu);
3331 3332

        if (info.cpuTime != 0) {
3333
            double cpuUsed = info.cpuTime;
3334

3335
            cpuUsed /= 1000000000.0;
3336

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

3340 3341
        if (info.maxMem != UINT_MAX)
            vshPrint(ctl, "%-15s %lu kB\n", _("Max memory:"),
3342
                 info.maxMem);
3343
        else
3344
            vshPrint(ctl, "%-15s %s\n", _("Max memory:"),
3345 3346
                 _("no limit"));

3347
        vshPrint(ctl, "%-15s %lu kB\n", _("Used memory:"),
3348 3349
                 info.memory);

K
Karel Zak 已提交
3350
    } else {
E
Eric Blake 已提交
3351
        ret = false;
K
Karel Zak 已提交
3352
    }
3353

3354 3355
    /* Check and display whether the domain is persistent or not */
    persistent = virDomainIsPersistent(dom);
3356 3357
    vshDebug(ctl, VSH_ERR_DEBUG, "Domain persistent flag value: %d\n",
             persistent);
3358 3359 3360 3361 3362 3363
    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 */
3364
    if (!virDomainGetAutostart(dom, &autostart)) {
3365
        vshPrint(ctl, "%-15s %s\n", _("Autostart:"),
3366 3367 3368
                 autostart ? _("enable") : _("disable") );
    }

3369 3370 3371 3372 3373 3374 3375
    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"));

3376 3377 3378
    /* Security model and label information */
    memset(&secmodel, 0, sizeof secmodel);
    if (virNodeGetSecurityModel(ctl->conn, &secmodel) == -1) {
3379 3380
        if (last_error->code != VIR_ERR_NO_SUPPORT) {
            virDomainFree(dom);
E
Eric Blake 已提交
3381
            return false;
3382 3383 3384
        } else {
            virFreeError(last_error);
            last_error = NULL;
3385
        }
3386 3387 3388 3389 3390 3391 3392
    } 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 */
3393
            if (VIR_ALLOC(seclabel) < 0) {
3394
                virDomainFree(dom);
E
Eric Blake 已提交
3395
                return false;
3396 3397 3398 3399 3400
            }

            if (virDomainGetSecurityLabel(dom, seclabel) == -1) {
                virDomainFree(dom);
                VIR_FREE(seclabel);
E
Eric Blake 已提交
3401
                return false;
3402
            } else {
3403
                if (seclabel->label[0] != '\0')
3404
                    vshPrint(ctl, "%-15s %s (%s)\n", _("Security label:"),
3405
                             seclabel->label, seclabel->enforcing ? "enforcing" : "permissive");
3406
            }
3407 3408

            VIR_FREE(seclabel);
3409 3410
        }
    }
3411
    virDomainFree(dom);
K
Karel Zak 已提交
3412 3413 3414
    return ret;
}

3415 3416 3417 3418
/*
 * "domjobinfo" command
 */
static const vshCmdInfo info_domjobinfo[] = {
3419 3420
    {"help", N_("domain job information")},
    {"desc", N_("Returns information about jobs running on a domain.")},
3421 3422 3423 3424
    {NULL, NULL}
};

static const vshCmdOptDef opts_domjobinfo[] = {
3425
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3426 3427 3428 3429
    {NULL, 0, 0, NULL}
};


E
Eric Blake 已提交
3430
static bool
3431 3432 3433 3434
cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
{
    virDomainJobInfo info;
    virDomainPtr dom;
E
Eric Blake 已提交
3435
    bool ret = true;
3436

3437
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3438
        return false;
3439 3440

    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
3441
        return false;
3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467

    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 已提交
3468
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
3469
            val = prettyCapacity(info.dataRemaining, &unit);
E
Eric Blake 已提交
3470
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
3471
            val = prettyCapacity(info.dataTotal, &unit);
E
Eric Blake 已提交
3472
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
3473 3474 3475
        }
        if (info.memTotal || info.memRemaining || info.memProcessed) {
            val = prettyCapacity(info.memProcessed, &unit);
E
Eric Blake 已提交
3476
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
3477
            val = prettyCapacity(info.memRemaining, &unit);
E
Eric Blake 已提交
3478
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
3479
            val = prettyCapacity(info.memTotal, &unit);
E
Eric Blake 已提交
3480
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
3481 3482 3483
        }
        if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
            val = prettyCapacity(info.fileProcessed, &unit);
E
Eric Blake 已提交
3484
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
3485
            val = prettyCapacity(info.fileRemaining, &unit);
E
Eric Blake 已提交
3486
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
3487
            val = prettyCapacity(info.fileTotal, &unit);
E
Eric Blake 已提交
3488
            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
3489 3490
        }
    } else {
E
Eric Blake 已提交
3491
        ret = false;
3492 3493 3494 3495 3496 3497
    }
cleanup:
    virDomainFree(dom);
    return ret;
}

3498 3499 3500 3501
/*
 * "domjobabort" command
 */
static const vshCmdInfo info_domjobabort[] = {
3502 3503
    {"help", N_("abort active domain job")},
    {"desc", N_("Aborts the currently running domain job")},
3504 3505 3506 3507
    {NULL, NULL}
};

static const vshCmdOptDef opts_domjobabort[] = {
3508
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3509 3510 3511
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3512
static bool
3513 3514 3515
cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
3516
    bool ret = true;
3517

3518
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3519
        return false;
3520 3521

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

    if (virDomainAbortJob(dom) < 0)
E
Eric Blake 已提交
3525
        ret = false;
3526 3527 3528 3529 3530

    virDomainFree(dom);
    return ret;
}

3531 3532 3533
/*
 * "freecell" command
 */
3534
static const vshCmdInfo info_freecell[] = {
3535 3536
    {"help", N_("NUMA free memory")},
    {"desc", N_("display available free memory for the NUMA cell.")},
3537 3538 3539
    {NULL, NULL}
};

3540
static const vshCmdOptDef opts_freecell[] = {
3541
    {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
3542
    {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
3543 3544 3545
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3546
static bool
3547
cmdFreecell(vshControl *ctl, const vshCmd *cmd)
3548
{
W
Wen Congyang 已提交
3549
    bool func_ret = false;
3550
    int ret;
3551
    int cell = -1, cell_given;
3552
    unsigned long long memory;
3553 3554 3555 3556
    xmlNodePtr *nodes = NULL;
    unsigned long nodes_cnt;
    unsigned long *nodes_id = NULL;
    unsigned long long *nodes_free = NULL;
3557
    int all_given;
3558 3559 3560 3561
    int i;
    char *cap_xml = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
3562

3563

3564
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3565
        return false;
3566

3567 3568 3569 3570
    if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
        vshError(ctl, "%s", _("cell number has to be a number"));
        goto cleanup;
    }
3571 3572 3573 3574 3575 3576 3577 3578 3579
    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) {
3580 3581 3582
        cap_xml = virConnectGetCapabilities(ctl->conn);
        if (!cap_xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
3583 3584 3585
            goto cleanup;
        }

3586
        xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
3587 3588
        if (!xml) {
            vshError(ctl, "%s", _("unable to get node capabilities"));
3589 3590
            goto cleanup;
        }
3591 3592 3593 3594
        nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
                                    ctxt, &nodes);

        if (nodes_cnt == -1) {
3595
            vshError(ctl, "%s", _("could not get information about "
3596
                                  "NUMA topology"));
3597 3598 3599
            goto cleanup;
        }

3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620
        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;
            }
        }

3621
        memory = 0;
3622 3623 3624 3625
        for (cell = 0; cell < nodes_cnt; cell++) {
            vshPrint(ctl, "%5lu: %10llu kB\n", nodes_id[cell],
                    (nodes_free[cell]/1024));
            memory += nodes_free[cell];
3626 3627 3628 3629
        }

        vshPrintExtra(ctl, "--------------------\n");
        vshPrintExtra(ctl, "%5s: %10llu kB\n", _("Total"), memory/1024);
3630
    } else {
3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644
        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));
3645 3646
    }

E
Eric Blake 已提交
3647
    func_ret = true;
3648

3649
cleanup:
3650
    xmlXPathFreeContext(ctxt);
3651
    xmlFreeDoc(xml);
3652
    VIR_FREE(nodes);
3653 3654 3655
    VIR_FREE(nodes_free);
    VIR_FREE(nodes_id);
    VIR_FREE(cap_xml);
3656
    return func_ret;
3657 3658
}

E
Eric Blake 已提交
3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672
/*
 * "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 已提交
3673
static bool
E
Eric Blake 已提交
3674 3675
cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
{
3676
    const char *type = NULL;
E
Eric Blake 已提交
3677 3678
    int vcpus;

3679 3680
    if (vshCommandOptString(cmd, "type", &type) < 0) {
        vshError(ctl, "%s", _("Invalid type"));
E
Eric Blake 已提交
3681
        return false;
3682
    }
E
Eric Blake 已提交
3683 3684

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3685
        return false;
E
Eric Blake 已提交
3686 3687 3688

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

E
Eric Blake 已提交
3692
    return true;
E
Eric Blake 已提交
3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706
}

/*
 * "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")},
3707
    {"active", VSH_OT_BOOL, 0, N_("get number of currently active vcpus")},
E
Eric Blake 已提交
3708
    {"live", VSH_OT_BOOL, 0, N_("get value from running domain")},
3709 3710 3711
    {"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 已提交
3712 3713 3714
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3715
static bool
E
Eric Blake 已提交
3716 3717 3718
cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
E
Eric Blake 已提交
3719
    bool ret = true;
E
Eric Blake 已提交
3720
    int maximum = vshCommandOptBool(cmd, "maximum");
3721
    int active = vshCommandOptBool(cmd, "active");
E
Eric Blake 已提交
3722 3723
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
3724 3725
    int current = vshCommandOptBool(cmd, "current");
    bool all = maximum + active + current + config + live == 0;
E
Eric Blake 已提交
3726 3727
    int count;

3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742
    /* 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 已提交
3743
        return false;
E
Eric Blake 已提交
3744
    }
3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757

    /* 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 已提交
3758
        vshError(ctl, "%s",
3759
                 _("--maximum and --active cannot both be specified"));
E
Eric Blake 已提交
3760
        return false;
E
Eric Blake 已提交
3761
    }
3762 3763 3764
    if (current + config + live > 1) {
        vshError(ctl, "%s",
                 _("--config, --live, and --current are mutually exclusive"));
E
Eric Blake 已提交
3765
        return false;
E
Eric Blake 已提交
3766 3767 3768
    }

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
3769
        return false;
E
Eric Blake 已提交
3770 3771

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

    /* In all cases, try the new API first; if it fails because we are
3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788
     * 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 已提交
3789 3790
    if (all || (maximum && config)) {
        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
3791
                                             VIR_DOMAIN_AFFECT_CONFIG));
E
Eric Blake 已提交
3792 3793 3794 3795 3796 3797 3798 3799 3800
        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;
            }
3801 3802
            virFreeError(last_error);
            last_error = NULL;
E
Eric Blake 已提交
3803 3804 3805 3806 3807
            VIR_FREE(xml);
        }

        if (count < 0) {
            virshReportError(ctl);
E
Eric Blake 已提交
3808
            ret = false;
E
Eric Blake 已提交
3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820
        } 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 |
3821
                                             VIR_DOMAIN_AFFECT_LIVE));
E
Eric Blake 已提交
3822 3823 3824 3825 3826 3827 3828
        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 已提交
3829
            ret = false;
E
Eric Blake 已提交
3830 3831 3832 3833 3834 3835 3836 3837 3838 3839
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

3840
    if (all || (active && config)) {
3841
        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_CONFIG);
E
Eric Blake 已提交
3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865
        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 已提交
3866
            ret = false;
E
Eric Blake 已提交
3867 3868 3869 3870 3871 3872 3873 3874 3875 3876
        } else if (all) {
            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"),
                     count);
        } else {
            vshPrint(ctl, "%d\n", count);
        }
        virFreeError(last_error);
        last_error = NULL;
    }

3877
    if (all || (active && live)) {
3878
        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_LIVE);
E
Eric Blake 已提交
3879 3880 3881 3882 3883 3884 3885 3886 3887
        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 已提交
3888
            ret = false;
E
Eric Blake 已提交
3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902
        } 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;
}

3903 3904 3905
/*
 * "vcpuinfo" command
 */
3906
static const vshCmdInfo info_vcpuinfo[] = {
E
Eric Blake 已提交
3907
    {"help", N_("detailed domain vcpu information")},
3908
    {"desc", N_("Returns basic information about the domain virtual CPUs.")},
3909 3910 3911
    {NULL, NULL}
};

3912
static const vshCmdOptDef opts_vcpuinfo[] = {
3913
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
3914 3915 3916
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
3917
static bool
3918
cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
3919 3920 3921 3922 3923
{
    virDomainInfo info;
    virDomainPtr dom;
    virNodeInfo nodeinfo;
    virVcpuInfoPtr cpuinfo;
3924 3925
    unsigned char *cpumaps;
    int ncpus, maxcpu;
3926
    size_t cpumaplen;
E
Eric Blake 已提交
3927
    bool ret = true;
3928
    int n, m;
3929

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

J
Jim Meyering 已提交
3933
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
3934
        return false;
3935 3936 3937

    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
3938
        return false;
3939 3940 3941 3942
    }

    if (virDomainGetInfo(dom, &info) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
3943
        return false;
3944 3945
    }

3946
    cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
3947 3948 3949
    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
    cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
3950

3951 3952
    if ((ncpus = virDomainGetVcpus(dom,
                                   cpuinfo, info.nrVirtCpu,
3953
                                   cpumaps, cpumaplen)) >= 0) {
3954 3955 3956 3957
        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 已提交
3958
                     _(vshDomainVcpuStateToString(cpuinfo[n].state)));
3959 3960 3961 3962 3963 3964 3965 3966
            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:"));
3967 3968
            for (m = 0; m < maxcpu; m++) {
                vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
3969 3970 3971 3972 3973 3974
            }
            vshPrint(ctl, "\n");
            if (n < (ncpus - 1)) {
                vshPrint(ctl, "\n");
            }
        }
3975
    } else {
3976
        if (info.state == VIR_DOMAIN_SHUTOFF &&
E
Eric Blake 已提交
3977
            (ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
3978 3979 3980
                                             cpumaps, cpumaplen,
                                             VIR_DOMAIN_AFFECT_CONFIG)) >= 0) {

E
Eric Blake 已提交
3981
            /* fallback plan to use virDomainGetVcpuPinInfo */
3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999

            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;
4000
        }
4001 4002
    }

4003
    VIR_FREE(cpumaps);
4004
    VIR_FREE(cpuinfo);
4005 4006 4007 4008 4009 4010 4011
    virDomainFree(dom);
    return ret;
}

/*
 * "vcpupin" command
 */
4012
static const vshCmdInfo info_vcpupin[] = {
4013
    {"help", N_("control or query domain vcpu affinity")},
4014
    {"desc", N_("Pin domain VCPUs to host physical CPUs.")},
4015 4016 4017
    {NULL, NULL}
};

4018
static const vshCmdOptDef opts_vcpupin[] = {
4019
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4020 4021 4022
    {"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")},
4023 4024 4025
    {"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")},
4026 4027 4028
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4029
static bool
E
Eric Blake 已提交
4030
cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
4031 4032 4033 4034
{
    virDomainInfo info;
    virDomainPtr dom;
    virNodeInfo nodeinfo;
4035
    int vcpu = -1;
4036
    const char *cpulist = NULL;
E
Eric Blake 已提交
4037
    bool ret = true;
4038 4039
    unsigned char *cpumap = NULL;
    unsigned char *cpumaps = NULL;
E
Eric Blake 已提交
4040
    size_t cpumaplen;
4041 4042
    bool bit, lastbit, isInvert;
    int i, cpu, lastcpu, maxcpu, ncpus;
4043
    bool unuse = false;
4044
    const char *cur;
4045 4046 4047
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    int current = vshCommandOptBool(cmd, "current");
4048
    bool query = false; /* Query mode if no cpulist */
E
Eric Blake 已提交
4049
    unsigned int flags = 0;
4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065

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

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

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

4073 4074
    if (vshCommandOptString(cmd, "cpulist", &cpulist) < 0) {
        vshError(ctl, "%s", _("vcpupin: Missing cpulist."));
4075
        virDomainFree(dom);
E
Eric Blake 已提交
4076
        return false;
4077
    }
4078
    query = !cpulist;
4079

4080 4081 4082 4083
    /* In query mode, "vcpu" is optional */
    if (vshCommandOptInt(cmd, "vcpu", &vcpu) < !query) {
        vshError(ctl, "%s",
                 _("vcpupin: Invalid or missing vCPU number."));
4084
        virDomainFree(dom);
E
Eric Blake 已提交
4085
        return false;
4086
    }
4087

4088 4089
    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
        virDomainFree(dom);
E
Eric Blake 已提交
4090
        return false;
4091 4092 4093
    }

    if (virDomainGetInfo(dom, &info) != 0) {
4094
        vshError(ctl, "%s", _("vcpupin: failed to get domain information."));
4095
        virDomainFree(dom);
E
Eric Blake 已提交
4096
        return false;
4097 4098 4099
    }

    if (vcpu >= info.nrVirtCpu) {
4100
        vshError(ctl, "%s", _("vcpupin: Invalid vCPU number."));
4101
        virDomainFree(dom);
E
Eric Blake 已提交
4102
        return false;
4103 4104
    }

4105 4106
    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
4107

4108 4109 4110 4111 4112 4113 4114 4115
    /* 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 已提交
4116
        if ((ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161
                                             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);
4162 4163
    /* Parse cpulist */
    cur = cpulist;
4164
    if (*cur == 0) {
4165
        goto parse_error;
4166 4167 4168
    } else if (*cur == 'r') {
        for (cpu = 0; cpu < maxcpu; cpu++)
            VIR_USE_CPU(cpumap, cpu);
4169
        cur = "";
4170
    }
4171 4172 4173 4174 4175 4176 4177

    while (*cur != 0) {

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

4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191
        /* 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);
4192

4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219
        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);
        }
4220

4221 4222 4223 4224 4225 4226
        if (*cur == ',') {
            cur++;
            virSkipSpaces(&cur);
            unuse = false;
        } else if (*cur == 0) {
            break;
4227
        } else {
4228
            goto parse_error;
4229
        }
4230
    }
4231

4232 4233 4234 4235 4236 4237 4238 4239
    if (flags == -1) {
        if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) {
            ret = false;
        }
    } else {
        if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0) {
            ret = false;
        }
4240 4241
    }

4242
cleanup:
4243
    VIR_FREE(cpumap);
4244 4245
    virDomainFree(dom);
    return ret;
4246 4247 4248 4249 4250

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

4253 4254 4255
/*
 * "setvcpus" command
 */
4256
static const vshCmdInfo info_setvcpus[] = {
4257 4258
    {"help", N_("change number of virtual CPUs")},
    {"desc", N_("Change the number of virtual CPUs in the guest domain.")},
4259 4260 4261
    {NULL, NULL}
};

4262
static const vshCmdOptDef opts_setvcpus[] = {
4263
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4264
    {"count", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of virtual CPUs")},
E
Eric Blake 已提交
4265 4266 4267
    {"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")},
4268
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
4269 4270 4271
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4272
static bool
4273
cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
4274 4275
{
    virDomainPtr dom;
4276
    int count = 0;
E
Eric Blake 已提交
4277
    bool ret = true;
E
Eric Blake 已提交
4278 4279 4280
    int maximum = vshCommandOptBool(cmd, "maximum");
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
4281
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
4282
    unsigned int flags = 0;
4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298

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

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

J
Jim Meyering 已提交
4303
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4304
        return false;
4305

4306 4307 4308 4309
    if (vshCommandOptInt(cmd, "count", &count) < 0) {
        vshError(ctl, "%s", _("Invalid number of virtual CPUs"));
        goto cleanup;
    }
4310

4311
    if (flags == -1) {
E
Eric Blake 已提交
4312
        if (virDomainSetVcpus(dom, count) != 0) {
E
Eric Blake 已提交
4313
            ret = false;
E
Eric Blake 已提交
4314 4315
        }
    } else {
4316 4317 4318
        /* If the --maximum flag was given, we need to ensure only the
           --config flag is in effect as well */
        if (maximum) {
4319
            vshDebug(ctl, VSH_ERR_DEBUG, "--maximum flag was given\n");
4320

4321 4322
            flags |= VIR_DOMAIN_VCPU_MAXIMUM;

4323 4324 4325 4326 4327 4328 4329 4330
            /* 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 已提交
4331
                ret = false;
4332 4333 4334 4335 4336
                goto cleanup;
            }
        }

        /* Apply the virtual cpu changes */
E
Eric Blake 已提交
4337
        if (virDomainSetVcpusFlags(dom, count, flags) < 0) {
E
Eric Blake 已提交
4338
            ret = false;
E
Eric Blake 已提交
4339
        }
4340 4341
    }

4342
  cleanup:
4343 4344 4345 4346
    virDomainFree(dom);
    return ret;
}

4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380
/*
 * "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;
}

4381 4382 4383 4384 4385
/*
 * "send-key" command
 */
static const vshCmdInfo info_send_key[] = {
    {"help", N_("Send keycodes to the guest")},
4386
    {"desc", N_("Send keycodes (integers or symbolic names) to the guest")},
4387 4388 4389 4390 4391
    {NULL, NULL}
};

static const vshCmdOptDef opts_send_key[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4392 4393
    {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT,
     N_("the codeset of keycodes, default:linux")},
4394
    {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT,
A
Alex Jia 已提交
4395
     N_("the time (in milliseconds) how long the keys will be held")},
4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467
    {"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;
}

4468 4469 4470
/*
 * "setmemory" command
 */
4471
static const vshCmdInfo info_setmem[] = {
4472 4473
    {"help", N_("change memory allocation")},
    {"desc", N_("Change the current memory allocation in the guest domain.")},
4474 4475 4476
    {NULL, NULL}
};

4477
static const vshCmdOptDef opts_setmem[] = {
4478
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4479
    {"kilobytes", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of kilobytes of memory")},
4480 4481
    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
4482
    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
4483 4484 4485
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4486
static bool
4487
cmdSetmem(vshControl *ctl, const vshCmd *cmd)
4488 4489
{
    virDomainPtr dom;
4490
    virDomainInfo info;
4491
    unsigned long kilobytes = 0;
E
Eric Blake 已提交
4492
    bool ret = true;
4493 4494
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
4495
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
4496
    unsigned int flags = 0;
4497

4498 4499 4500
    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
E
Eric Blake 已提交
4501
            return false;
4502
        }
4503
        flags = VIR_DOMAIN_AFFECT_CURRENT;
4504 4505
    } else {
        if (config)
4506
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
4507
        if (live)
4508
            flags |= VIR_DOMAIN_AFFECT_LIVE;
4509 4510 4511
        /* neither option is specified */
        if (!live && !config)
            flags = -1;
4512
    }
4513

4514
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4515
        return false;
4516

J
Jim Meyering 已提交
4517
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4518
        return false;
4519

4520 4521
    if (vshCommandOptUL(cmd, "kilobytes", &kilobytes) < 0) {
        vshError(ctl, "%s", _("memory size has to be a number"));
E
Eric Blake 已提交
4522
        return false;
4523 4524
    }

4525
    if (kilobytes <= 0) {
4526
        virDomainFree(dom);
4527
        vshError(ctl, _("Invalid value of %lu for memory size"), kilobytes);
E
Eric Blake 已提交
4528
        return false;
4529 4530
    }

4531 4532
    if (virDomainGetInfo(dom, &info) != 0) {
        virDomainFree(dom);
4533
        vshError(ctl, "%s", _("Unable to verify MaxMemorySize"));
E
Eric Blake 已提交
4534
        return false;
4535 4536 4537 4538
    }

    if (kilobytes > info.maxMem) {
        virDomainFree(dom);
4539
        vshError(ctl, _("Requested memory size %lu kb is larger than maximum of %lu kb"),
4540
                 kilobytes, info.maxMem);
E
Eric Blake 已提交
4541
        return false;
4542 4543
    }

4544
    if (flags == -1) {
4545
        if (virDomainSetMemory(dom, kilobytes) != 0) {
E
Eric Blake 已提交
4546
            ret = false;
4547 4548 4549
        }
    } else {
        if (virDomainSetMemoryFlags(dom, kilobytes, flags) < 0) {
E
Eric Blake 已提交
4550
            ret = false;
4551
        }
4552 4553 4554 4555 4556 4557 4558 4559 4560
    }

    virDomainFree(dom);
    return ret;
}

/*
 * "setmaxmem" command
 */
4561
static const vshCmdInfo info_setmaxmem[] = {
4562 4563
    {"help", N_("change maximum memory limit")},
    {"desc", N_("Change the maximum memory allocation limit in the guest domain.")},
4564 4565 4566
    {NULL, NULL}
};

4567
static const vshCmdOptDef opts_setmaxmem[] = {
4568
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
4569
    {"kilobytes", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum memory limit in kilobytes")},
4570 4571 4572
    {"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")},
4573 4574 4575
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4576
static bool
4577
cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
4578 4579
{
    virDomainPtr dom;
4580
    int kilobytes = 0;
E
Eric Blake 已提交
4581
    bool ret = true;
4582 4583 4584
    int config = vshCommandOptBool(cmd, "config");
    int live = vshCommandOptBool(cmd, "live");
    int current = vshCommandOptBool(cmd, "current");
E
Eric Blake 已提交
4585
    unsigned int flags = VIR_DOMAIN_MEM_MAXIMUM;
4586 4587 4588 4589

    if (current) {
        if (live || config) {
            vshError(ctl, "%s", _("--current must be specified exclusively"));
E
Eric Blake 已提交
4590
            return false;
4591 4592 4593
        }
    } else {
        if (config)
4594
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
4595
        if (live)
4596
            flags |= VIR_DOMAIN_AFFECT_LIVE;
4597 4598 4599 4600
        /* neither option is specified */
        if (!live && !config)
            flags = -1;
    }
4601

4602
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4603
        return false;
4604

J
Jim Meyering 已提交
4605
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
4606
        return false;
4607

4608 4609
    if (vshCommandOptInt(cmd, "kilobytes", &kilobytes) < 0) {
        vshError(ctl, "%s", _("memory size has to be a number"));
E
Eric Blake 已提交
4610
        return false;
4611 4612
    }

4613
    if (kilobytes <= 0) {
4614
        virDomainFree(dom);
4615
        vshError(ctl, _("Invalid value of %d for memory size"), kilobytes);
E
Eric Blake 已提交
4616
        return false;
4617 4618
    }

4619 4620 4621
    if (flags == -1) {
        if (virDomainSetMaxMemory(dom, kilobytes) != 0) {
            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
E
Eric Blake 已提交
4622
            ret = false;
4623 4624 4625 4626
        }
    } else {
        if (virDomainSetMemoryFlags(dom, kilobytes, flags) < 0) {
            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
E
Eric Blake 已提交
4627
            ret = false;
4628
        }
4629 4630
    }

4631 4632 4633 4634
    virDomainFree(dom);
    return ret;
}

4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650
/*
 * "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]")},
H
Hu Tao 已提交
4651 4652 4653
    {"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")},
4654 4655 4656
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4657
static bool
4658 4659 4660 4661 4662
cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
{
    virDomainPtr dom;
    int weight = 0;
    int nparams = 0;
4663
    int rv = 0;
4664
    unsigned int i = 0;
4665
    virTypedParameterPtr params = NULL, temp = NULL;
E
Eric Blake 已提交
4666
    bool ret = false;
H
Hu Tao 已提交
4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683
    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;
    }
4684 4685

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4686
        return false;
4687 4688

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

4691
    if ((rv = vshCommandOptInt(cmd, "weight", &weight)) < 0) {
4692 4693 4694 4695 4696
        vshError(ctl, "%s",
                 _("Unable to parse integer parameter"));
        goto cleanup;
    }

4697
    if (rv > 0) {
4698
        nparams++;
4699
        if (weight <= 0) {
4700 4701 4702 4703 4704 4705 4706
            vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
            goto cleanup;
        }
    }

    if (nparams == 0) {
        /* get the number of blkio parameters */
H
Hu Tao 已提交
4707
        if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
4708 4709 4710 4711 4712 4713 4714
            vshError(ctl, "%s",
                     _("Unable to get number of blkio parameters"));
            goto cleanup;
        }

        if (nparams == 0) {
            /* nothing to output */
E
Eric Blake 已提交
4715
            ret = true;
4716 4717 4718 4719 4720
            goto cleanup;
        }

        /* now go get all the blkio parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));
H
Hu Tao 已提交
4721
        if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
4722 4723 4724 4725 4726 4727
            vshError(ctl, "%s", _("Unable to get blkio parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
            switch (params[i].type) {
4728
                case VIR_TYPED_PARAM_INT:
4729 4730 4731
                    vshPrint(ctl, "%-15s: %d\n", params[i].field,
                             params[i].value.i);
                    break;
4732
                case VIR_TYPED_PARAM_UINT:
4733 4734 4735
                    vshPrint(ctl, "%-15s: %u\n", params[i].field,
                             params[i].value.ui);
                    break;
4736
                case VIR_TYPED_PARAM_LLONG:
4737 4738 4739
                    vshPrint(ctl, "%-15s: %lld\n", params[i].field,
                             params[i].value.l);
                    break;
4740
                case VIR_TYPED_PARAM_ULLONG:
4741 4742 4743
                    vshPrint(ctl, "%-15s: %llu\n", params[i].field,
                                 params[i].value.ul);
                    break;
4744
                case VIR_TYPED_PARAM_DOUBLE:
4745 4746 4747
                    vshPrint(ctl, "%-15s: %f\n", params[i].field,
                             params[i].value.d);
                    break;
4748
                case VIR_TYPED_PARAM_BOOLEAN:
4749 4750 4751 4752 4753 4754 4755 4756
                    vshPrint(ctl, "%-15s: %d\n", params[i].field,
                             params[i].value.b);
                    break;
                default:
                    vshPrint(ctl, "unimplemented blkio parameter type\n");
            }
        }

E
Eric Blake 已提交
4757
        ret = true;
4758 4759 4760 4761 4762 4763
    } else {
        /* set the blkio parameters */
        params = vshCalloc(ctl, nparams, sizeof(*params));

        for (i = 0; i < nparams; i++) {
            temp = &params[i];
4764
            temp->type = VIR_TYPED_PARAM_UINT;
4765 4766 4767 4768 4769 4770 4771 4772

            if (weight) {
                temp->value.ui = weight;
                strncpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
                        sizeof(temp->field));
                weight = 0;
            }
        }
H
Hu Tao 已提交
4773
        if (virDomainSetBlkioParameters(dom, params, nparams, flags) != 0)
4774 4775
            vshError(ctl, "%s", _("Unable to change blkio parameters"));
        else
E
Eric Blake 已提交
4776
            ret = true;
4777 4778 4779 4780 4781 4782 4783 4784
    }

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

4785 4786 4787 4788
/*
 * "memtune" command
 */
static const vshCmdInfo info_memtune[] = {
4789 4790 4791
    {"help", N_("Get or set memory parameters")},
    {"desc", N_("Get or set the current memory parameters for a guest" \
                " domain.\n" \
4792 4793 4794 4795 4796 4797 4798
                "    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")},
4799
    {"hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
4800
     N_("Max memory in kilobytes")},
4801
    {"soft-limit", VSH_OT_INT, VSH_OFLAG_NONE,
4802
     N_("Memory during contention in kilobytes")},
4803
    {"swap-hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
4804
     N_("Max memory plus swap in kilobytes")},
4805
    {"min-guarantee", VSH_OT_INT, VSH_OFLAG_NONE,
4806
     N_("Min guaranteed memory in kilobytes")},
4807 4808 4809
    {"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")},
4810 4811 4812
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
4813
static bool
4814 4815 4816
cmdMemtune(vshControl * ctl, const vshCmd * cmd)
{
    virDomainPtr dom;
4817 4818
    long long hard_limit = 0, soft_limit = 0, swap_hard_limit = 0;
    long long min_guarantee = 0;
4819 4820
    int nparams = 0;
    unsigned int i = 0;
4821
    virTypedParameterPtr params = NULL, temp = NULL;
E
Eric Blake 已提交
4822
    bool ret = false;
4823 4824 4825 4826 4827 4828 4829 4830 4831 4832
    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;
        }
4833
        flags = VIR_DOMAIN_AFFECT_CURRENT;
4834 4835
    } else {
        if (config)
4836
            flags |= VIR_DOMAIN_AFFECT_CONFIG;
4837
        if (live)
4838
            flags |= VIR_DOMAIN_AFFECT_LIVE;
4839
    }
4840 4841

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4842
        return false;
4843 4844

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

4847 4848 4849 4850 4851 4852 4853 4854 4855
    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;
    }

4856 4857 4858 4859 4860 4861 4862 4863 4864
    if (hard_limit)
        nparams++;

    if (soft_limit)
        nparams++;

    if (swap_hard_limit)
        nparams++;

4865 4866 4867
    if (min_guarantee)
        nparams++;

4868 4869
    if (nparams == 0) {
        /* get the number of memory parameters */
4870
        if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
4871 4872 4873 4874 4875
            vshError(ctl, "%s",
                     _("Unable to get number of memory parameters"));
            goto cleanup;
        }

4876 4877
        if (nparams == 0) {
            /* nothing to output */
E
Eric Blake 已提交
4878
            ret = true;
4879 4880 4881
            goto cleanup;
        }

4882
        /* now go get all the memory parameters */
E
Eric Blake 已提交
4883
        params = vshCalloc(ctl, nparams, sizeof(*params));
4884
        if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
4885 4886 4887 4888 4889 4890
            vshError(ctl, "%s", _("Unable to get memory parameters"));
            goto cleanup;
        }

        for (i = 0; i < nparams; i++) {
            switch (params[i].type) {
4891
                case VIR_TYPED_PARAM_INT:
4892 4893 4894
                    vshPrint(ctl, "%-15s: %d\n", params[i].field,
                             params[i].value.i);
                    break;
4895
                case VIR_TYPED_PARAM_UINT:
4896 4897 4898
                    vshPrint(ctl, "%-15s: %u\n", params[i].field,
                             params[i].value.ui);
                    break;
4899
                case VIR_TYPED_PARAM_LLONG:
4900 4901 4902
                    vshPrint(ctl, "%-15s: %lld\n", params[i].field,
                             params[i].value.l);
                    break;
4903
                case VIR_TYPED_PARAM_ULLONG:
4904 4905 4906 4907 4908
                    if (params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED)
                        vshPrint(ctl, "%-15s: unlimited\n", params[i].field);
                    else
                        vshPrint(ctl, "%-15s: %llu kB\n", params[i].field,
                                 params[i].value.ul);
4909
                    break;
4910
                case VIR_TYPED_PARAM_DOUBLE:
4911 4912 4913
                    vshPrint(ctl, "%-15s: %f\n", params[i].field,
                             params[i].value.d);
                    break;
4914
                case VIR_TYPED_PARAM_BOOLEAN:
4915 4916 4917 4918
                    vshPrint(ctl, "%-15s: %d\n", params[i].field,
                             params[i].value.b);
                    break;
                default:
4919
                    vshPrint(ctl, "unimplemented memory parameter type\n");
4920 4921 4922
            }
        }

E
Eric Blake 已提交
4923
        ret = true;
4924 4925
    } else {
        /* set the memory parameters */
E
Eric Blake 已提交
4926
        params = vshCalloc(ctl, nparams, sizeof(*params));
4927 4928 4929

        for (i = 0; i < nparams; i++) {
            temp = &params[i];
4930
            temp->type = VIR_TYPED_PARAM_ULLONG;
4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949

            /*
             * 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;
4950
                strncpy(temp->field, VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
4951 4952
                        sizeof(temp->field));
                swap_hard_limit = 0;
4953 4954 4955 4956 4957
            } else if (min_guarantee) {
                temp->value.ul = min_guarantee;
                strncpy(temp->field, VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
                        sizeof(temp->field));
                min_guarantee = 0;
4958
            }
4959 4960 4961 4962

            /* If the user has passed -1, we interpret it as unlimited */
            if (temp->value.ul == -1)
                temp->value.ul = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
4963
        }
4964
        if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
4965
            vshError(ctl, "%s", _("Unable to change memory parameters"));
4966
        else
E
Eric Blake 已提交
4967
            ret = true;
4968 4969 4970
    }

  cleanup:
E
Eric Blake 已提交
4971
    VIR_FREE(params);
4972 4973 4974 4975
    virDomainFree(dom);
    return ret;
}

4976 4977 4978
/*
 * "nodeinfo" command
 */
4979
static const vshCmdInfo info_nodeinfo[] = {
4980 4981
    {"help", N_("node information")},
    {"desc", N_("Returns basic information about the node.")},
4982 4983 4984
    {NULL, NULL}
};

E
Eric Blake 已提交
4985
static bool
4986
cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
4987 4988
{
    virNodeInfo info;
4989

4990
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
4991
        return false;
4992 4993

    if (virNodeGetInfo(ctl->conn, &info) < 0) {
4994
        vshError(ctl, "%s", _("failed to get node information"));
E
Eric Blake 已提交
4995
        return false;
4996 4997 4998 4999 5000 5001 5002 5003 5004 5005
    }
    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 已提交
5006
    return true;
5007 5008
}

5009 5010 5011 5012 5013
/*
 * "nodecpustats" command
 */
static const vshCmdInfo info_nodecpustats[] = {
    {"help", N_("Prints cpu stats of the node.")},
5014
    {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
5015 5016 5017 5018 5019 5020 5021 5022 5023 5024
    {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
5025
cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
5026 5027 5028 5029
{
    int i, j;
    bool flag_utilization = false;
    bool flag_percent = vshCommandOptBool(cmd, "percent");
5030 5031
    int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
    virNodeCPUStatsPtr params;
5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074
    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;

5075
            if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
5076
                cpu_stats[i].sys = value;
5077
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
5078
                cpu_stats[i].user = value;
5079
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
5080
                cpu_stats[i].idle = value;
5081
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
5082
                cpu_stats[i].iowait = value;
5083
            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097
                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) {
5098
            vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
5099
            vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
5100
            vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
5101 5102 5103 5104 5105 5106 5107
            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);
5108
            vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120
        } 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",
5121
                     _("user:"), user_time / total_time * 100);
5122
            vshPrint(ctl, "%-15s %5.1lf%%\n",
5123
                     _("system:"), sys_time  / total_time * 100);
5124
            vshPrint(ctl, "%-15s %5.1lf%%\n",
5125
                     _("idle:"), idle_time     / total_time * 100);
5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137
            vshPrint(ctl, "%-15s %5.1lf%%\n",
                     _("iowait:"), iowait_time   / total_time * 100);
        }
    }

    ret = true;

  cleanup:
    VIR_FREE(params);
    return ret;
}

5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156
/*
 * "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;
5157 5158
    int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
    virNodeMemoryStatsPtr params = NULL;
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
    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;
}

5199 5200 5201
/*
 * "capabilities" command
 */
5202
static const vshCmdInfo info_capabilities[] = {
5203 5204
    {"help", N_("capabilities")},
    {"desc", N_("Returns capabilities of hypervisor/driver.")},
5205 5206 5207
    {NULL, NULL}
};

E
Eric Blake 已提交
5208
static bool
5209
cmdCapabilities (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
5210 5211 5212
{
    char *caps;

5213
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5214
        return false;
5215 5216

    if ((caps = virConnectGetCapabilities (ctl->conn)) == NULL) {
5217
        vshError(ctl, "%s", _("failed to get capabilities"));
E
Eric Blake 已提交
5218
        return false;
5219 5220
    }
    vshPrint (ctl, "%s\n", caps);
5221
    VIR_FREE(caps);
5222

E
Eric Blake 已提交
5223
    return true;
5224 5225
}

5226 5227 5228
/*
 * "dumpxml" command
 */
5229
static const vshCmdInfo info_dumpxml[] = {
5230 5231
    {"help", N_("domain information in XML")},
    {"desc", N_("Output the domain information as an XML dump to stdout.")},
5232
    {NULL, NULL}
5233 5234
};

5235
static const vshCmdOptDef opts_dumpxml[] = {
5236 5237 5238
    {"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")},
5239
    {"update-cpu", VSH_OT_BOOL, 0, N_("update guest CPU according to host CPU")},
5240
    {NULL, 0, 0, NULL}
5241 5242
};

E
Eric Blake 已提交
5243
static bool
5244
cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
5245
{
5246
    virDomainPtr dom;
E
Eric Blake 已提交
5247
    bool ret = true;
5248
    char *dump;
E
Eric Blake 已提交
5249
    unsigned int flags = 0;
5250 5251
    int inactive = vshCommandOptBool(cmd, "inactive");
    int secure = vshCommandOptBool(cmd, "security-info");
5252
    int update = vshCommandOptBool(cmd, "update-cpu");
5253 5254 5255 5256 5257

    if (inactive)
        flags |= VIR_DOMAIN_XML_INACTIVE;
    if (secure)
        flags |= VIR_DOMAIN_XML_SECURE;
5258 5259
    if (update)
        flags |= VIR_DOMAIN_XML_UPDATE_CPU;
5260

5261
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5262
        return false;
5263

J
Jim Meyering 已提交
5264
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
5265
        return false;
5266

5267
    dump = virDomainGetXMLDesc(dom, flags);
5268
    if (dump != NULL) {
5269
        vshPrint(ctl, "%s", dump);
5270
        VIR_FREE(dump);
5271
    } else {
E
Eric Blake 已提交
5272
        ret = false;
5273
    }
5274

5275 5276 5277 5278
    virDomainFree(dom);
    return ret;
}

5279 5280 5281 5282
/*
 * "domxml-from-native" command
 */
static const vshCmdInfo info_domxmlfromnative[] = {
5283 5284
    {"help", N_("Convert native config to domain XML")},
    {"desc", N_("Convert native guest configuration format to domain XML format.")},
5285 5286 5287 5288
    {NULL, NULL}
};

static const vshCmdOptDef opts_domxmlfromnative[] = {
5289 5290
    {"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")},
5291 5292 5293
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5294
static bool
5295 5296
cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
5297
    bool ret = true;
5298 5299
    const char *format = NULL;
    const char *configFile = NULL;
5300 5301
    char *configData;
    char *xmlData;
E
Eric Blake 已提交
5302
    unsigned int flags = 0;
5303

5304
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5305
        return false;
5306

5307 5308
    if (vshCommandOptString(cmd, "format", &format) < 0 ||
        vshCommandOptString(cmd, "config", &configFile) < 0)
E
Eric Blake 已提交
5309
        return false;
5310

5311
    if (virFileReadAll(configFile, 1024*1024, &configData) < 0)
E
Eric Blake 已提交
5312
        return false;
5313 5314 5315

    xmlData = virConnectDomainXMLFromNative(ctl->conn, format, configData, flags);
    if (xmlData != NULL) {
5316
        vshPrint(ctl, "%s", xmlData);
5317
        VIR_FREE(xmlData);
5318
    } else {
E
Eric Blake 已提交
5319
        ret = false;
5320 5321 5322 5323 5324 5325 5326 5327 5328
    }

    return ret;
}

/*
 * "domxml-to-native" command
 */
static const vshCmdInfo info_domxmltonative[] = {
5329 5330
    {"help", N_("Convert domain XML to native config")},
    {"desc", N_("Convert domain XML config to a native guest configuration format.")},
5331 5332 5333 5334
    {NULL, NULL}
};

static const vshCmdOptDef opts_domxmltonative[] = {
5335 5336
    {"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")},
5337 5338 5339
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5340
static bool
5341 5342
cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
5343
    bool ret = true;
5344 5345
    const char *format = NULL;
    const char *xmlFile = NULL;
5346 5347
    char *configData;
    char *xmlData;
E
Eric Blake 已提交
5348
    unsigned int flags = 0;
5349

5350
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5351
        return false;
5352

5353 5354
    if (vshCommandOptString(cmd, "format", &format) < 0
        || vshCommandOptString(cmd, "xml", &xmlFile) < 0)
E
Eric Blake 已提交
5355
        return false;
5356

5357
    if (virFileReadAll(xmlFile, 1024*1024, &xmlData) < 0)
E
Eric Blake 已提交
5358
        return false;
5359 5360 5361

    configData = virConnectDomainXMLToNative(ctl->conn, format, xmlData, flags);
    if (configData != NULL) {
5362
        vshPrint(ctl, "%s", configData);
5363
        VIR_FREE(configData);
5364
    } else {
E
Eric Blake 已提交
5365
        ret = false;
5366 5367 5368 5369 5370
    }

    return ret;
}

K
Karel Zak 已提交
5371
/*
K
Karel Zak 已提交
5372
 * "domname" command
K
Karel Zak 已提交
5373
 */
5374
static const vshCmdInfo info_domname[] = {
5375
    {"help", N_("convert a domain id or UUID to domain name")},
5376
    {"desc", ""},
5377
    {NULL, NULL}
K
Karel Zak 已提交
5378 5379
};

5380
static const vshCmdOptDef opts_domname[] = {
5381
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or uuid")},
5382
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
5383 5384
};

E
Eric Blake 已提交
5385
static bool
5386
cmdDomname(vshControl *ctl, const vshCmd *cmd)
5387
{
K
Karel Zak 已提交
5388 5389
    virDomainPtr dom;

5390
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5391
        return false;
J
Jim Meyering 已提交
5392
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
5393
                                      VSH_BYID|VSH_BYUUID)))
E
Eric Blake 已提交
5394
        return false;
5395

K
Karel Zak 已提交
5396 5397
    vshPrint(ctl, "%s\n", virDomainGetName(dom));
    virDomainFree(dom);
E
Eric Blake 已提交
5398
    return true;
K
Karel Zak 已提交
5399 5400 5401
}

/*
K
Karel Zak 已提交
5402
 * "domid" command
K
Karel Zak 已提交
5403
 */
5404
static const vshCmdInfo info_domid[] = {
5405
    {"help", N_("convert a domain name or UUID to domain id")},
5406
    {"desc", ""},
5407
    {NULL, NULL}
K
Karel Zak 已提交
5408 5409
};

5410
static const vshCmdOptDef opts_domid[] = {
5411
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
5412
    {NULL, 0, 0, NULL}
K
Karel Zak 已提交
5413 5414
};

E
Eric Blake 已提交
5415
static bool
5416
cmdDomid(vshControl *ctl, const vshCmd *cmd)
5417
{
5418
    virDomainPtr dom;
5419
    unsigned int id;
K
Karel Zak 已提交
5420

5421
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5422
        return false;
J
Jim Meyering 已提交
5423
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
5424
                                      VSH_BYNAME|VSH_BYUUID)))
E
Eric Blake 已提交
5425
        return false;
5426

5427 5428
    id = virDomainGetID(dom);
    if (id == ((unsigned int)-1))
5429
        vshPrint(ctl, "%s\n", "-");
5430
    else
5431
        vshPrint(ctl, "%d\n", id);
K
Karel Zak 已提交
5432
    virDomainFree(dom);
E
Eric Blake 已提交
5433
    return true;
K
Karel Zak 已提交
5434
}
5435

K
Karel Zak 已提交
5436 5437 5438
/*
 * "domuuid" command
 */
5439
static const vshCmdInfo info_domuuid[] = {
5440
    {"help", N_("convert a domain name or id to domain UUID")},
5441
    {"desc", ""},
K
Karel Zak 已提交
5442 5443 5444
    {NULL, NULL}
};

5445
static const vshCmdOptDef opts_domuuid[] = {
5446
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or name")},
K
Karel Zak 已提交
5447 5448 5449
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5450
static bool
5451
cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
K
Karel Zak 已提交
5452 5453
{
    virDomainPtr dom;
5454
    char uuid[VIR_UUID_STRING_BUFLEN];
K
Karel Zak 已提交
5455

5456
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5457
        return false;
J
Jim Meyering 已提交
5458
    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
5459
                                      VSH_BYNAME|VSH_BYID)))
E
Eric Blake 已提交
5460
        return false;
5461

K
Karel Zak 已提交
5462 5463 5464
    if (virDomainGetUUIDString(dom, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
5465
        vshError(ctl, "%s", _("failed to get domain UUID"));
5466

5467
    virDomainFree(dom);
E
Eric Blake 已提交
5468
    return true;
K
Karel Zak 已提交
5469 5470
}

5471 5472 5473
/*
 * "migrate" command
 */
5474
static const vshCmdInfo info_migrate[] = {
5475 5476
    {"help", N_("migrate domain to another host")},
    {"desc", N_("Migrate domain to another host.  Add --live for live migration.")},
5477 5478 5479
    {NULL, NULL}
};

5480
static const vshCmdOptDef opts_migrate[] = {
5481 5482 5483 5484 5485 5486 5487
    {"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")},
5488 5489
    {"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)")},
5490 5491
    {"change-protection", VSH_OT_BOOL, 0,
     N_("prevent any configuration changes to domain until migration ends)")},
W
Wen Congyang 已提交
5492
    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of migration")},
5493
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
5494
    {"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)")},
5495 5496
    {"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 已提交
5497
    {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")},
5498
    {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")},
5499 5500 5501
    {NULL, 0, 0, NULL}
};

5502 5503 5504 5505 5506 5507 5508 5509
typedef struct __vshCtrlData {
    vshControl *ctl;
    const vshCmd *cmd;
    int writefd;
} vshCtrlData;

static void
doMigrate (void *opaque)
5510
{
5511
    char ret = '1';
5512
    virDomainPtr dom = NULL;
5513 5514 5515
    const char *desturi = NULL;
    const char *migrateuri = NULL;
    const char *dname = NULL;
E
Eric Blake 已提交
5516
    unsigned int flags = 0;
5517 5518 5519
    vshCtrlData *data = opaque;
    vshControl *ctl = data->ctl;
    const vshCmd *cmd = data->cmd;
5520 5521
    const char *xmlfile = NULL;
    char *xml = NULL;
5522
    sigset_t sigmask, oldsigmask;
5523 5524 5525 5526 5527

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

5529
    if (!vshConnectionUsability (ctl, ctl->conn))
5530
        goto out;
5531

J
Jim Meyering 已提交
5532
    if (!(dom = vshCommandOptDomain (ctl, cmd, NULL)))
5533
        goto out;
5534

5535 5536
    if (vshCommandOptString(cmd, "desturi", &desturi) <= 0 ||
        vshCommandOptString(cmd, "migrateuri", &migrateuri) < 0 ||
5537 5538
        vshCommandOptString(cmd, "dname", &dname) < 0) {
        vshError(ctl, "%s", _("missing argument"));
5539
        goto out;
5540
    }
5541

5542 5543 5544 5545 5546
    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
        vshError(ctl, "%s", _("malformed xml argument"));
        goto out;
    }

5547 5548
    if (vshCommandOptBool (cmd, "live"))
        flags |= VIR_MIGRATE_LIVE;
5549 5550
    if (vshCommandOptBool (cmd, "p2p"))
        flags |= VIR_MIGRATE_PEER2PEER;
C
Chris Lalancette 已提交
5551
    if (vshCommandOptBool (cmd, "tunnelled"))
5552
        flags |= VIR_MIGRATE_TUNNELLED;
C
Chris Lalancette 已提交
5553

C
Chris Lalancette 已提交
5554 5555 5556 5557 5558
    if (vshCommandOptBool (cmd, "persistent"))
        flags |= VIR_MIGRATE_PERSIST_DEST;
    if (vshCommandOptBool (cmd, "undefinesource"))
        flags |= VIR_MIGRATE_UNDEFINE_SOURCE;

5559 5560 5561
    if (vshCommandOptBool (cmd, "suspend"))
        flags |= VIR_MIGRATE_PAUSED;

5562 5563 5564 5565 5566 5567
    if (vshCommandOptBool (cmd, "copy-storage-all"))
        flags |= VIR_MIGRATE_NON_SHARED_DISK;

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

5568 5569
    if (vshCommandOptBool (cmd, "change-protection"))
        flags |= VIR_MIGRATE_CHANGE_PROTECTION;
5570 5571 5572 5573 5574 5575

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


5576 5577 5578 5579
    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. */
5580

5581
        if (migrateuri != NULL) {
J
Jim Fehlig 已提交
5582
            vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration"));
5583
            goto out;
5584
        }
5585

5586
        if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0)
5587
            ret = '0';
5588 5589 5590 5591 5592 5593
    } else {
        /* For traditional live migration, connect to the destination host directly. */
        virConnectPtr dconn = NULL;
        virDomainPtr ddom = NULL;

        dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0);
5594
        if (!dconn) goto out;
5595

5596
        ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0);
5597 5598
        if (ddom) {
            virDomainFree(ddom);
5599
            ret = '0';
5600 5601 5602
        }
        virConnectClose (dconn);
    }
5603

5604 5605 5606
out:
    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
out_sig:
5607
    if (dom) virDomainFree (dom);
5608
    VIR_FREE(xml);
5609 5610 5611
    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
}

W
Wen Congyang 已提交
5612
static void
5613 5614
print_job_progress(const char *label, unsigned long long remaining,
                   unsigned long long total)
W
Wen Congyang 已提交
5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633
{
    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;
        }
    }

5634 5635
    /* see comments in vshError about why we must flush */
    fflush(stdout);
5636
    fprintf(stderr, "\r%s: [%3d %%]", label, progress);
5637
    fflush(stderr);
W
Wen Congyang 已提交
5638 5639
}

E
Eric Blake 已提交
5640
static bool
5641 5642 5643 5644 5645
cmdMigrate (vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    int p[2] = {-1, -1};
    int ret = -1;
W
Wen Congyang 已提交
5646
    bool functionReturn = false;
5647 5648 5649 5650 5651
    virThread workerThread;
    struct pollfd pollfd;
    char retchar;
    struct sigaction sig_action;
    struct sigaction old_sig_action;
W
Wen Congyang 已提交
5652 5653
    virDomainJobInfo jobinfo;
    bool verbose = false;
5654
    int timeout = 0;
W
Wen Congyang 已提交
5655 5656
    struct timeval start, curr;
    bool live_flag = false;
5657
    vshCtrlData data;
5658 5659 5660 5661
    sigset_t sigmask, oldsigmask;

    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);
5662 5663

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

W
Wen Congyang 已提交
5666 5667 5668
    if (vshCommandOptBool (cmd, "verbose"))
        verbose = true;

W
Wen Congyang 已提交
5669
    if (vshCommandOptBool (cmd, "live"))
E
Eric Blake 已提交
5670
        live_flag = true;
5671
    if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
W
Wen Congyang 已提交
5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688
        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;
        }
    }

5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711
    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;

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

    pollfd.fd = p[0];
    pollfd.events = POLLIN;
    pollfd.revents = 0;

W
Wen Congyang 已提交
5712
    GETTIMEOFDAY(&start);
W
Wen Congyang 已提交
5713
    while (1) {
5714
repoll:
W
Wen Congyang 已提交
5715 5716 5717 5718
        ret = poll(&pollfd, 1, 500);
        if (ret > 0) {
            if (saferead(p[0], &retchar, sizeof(retchar)) > 0) {
                if (retchar == '0') {
W
Wen Congyang 已提交
5719
                    functionReturn = true;
W
Wen Congyang 已提交
5720 5721
                    if (verbose) {
                        /* print [100 %] */
5722
                        print_job_progress("Migration", 0, 1);
W
Wen Congyang 已提交
5723 5724
                    }
                } else
W
Wen Congyang 已提交
5725
                    functionReturn = false;
5726
            } else
W
Wen Congyang 已提交
5727
                functionReturn = false;
W
Wen Congyang 已提交
5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738
            break;
        }

        if (ret < 0) {
            if (errno == EINTR) {
                if (intCaught) {
                    virDomainAbortJob(dom);
                    intCaught = 0;
                } else
                    goto repoll;
            }
W
Wen Congyang 已提交
5739
            functionReturn = false;
W
Wen Congyang 已提交
5740 5741 5742
            break;
        }

W
Wen Congyang 已提交
5743 5744 5745 5746
        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. */
5747 5748
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "suspend the domain when migration timeouts\n");
W
Wen Congyang 已提交
5749 5750 5751 5752
            virDomainSuspend(dom);
            timeout = 0;
        }

W
Wen Congyang 已提交
5753 5754 5755 5756 5757
        if (verbose) {
            pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
            ret = virDomainGetJobInfo(dom, &jobinfo);
            pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
            if (ret == 0)
5758 5759
                print_job_progress("Migration", jobinfo.dataRemaining,
                                   jobinfo.dataTotal);
5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770
        }
    }

    sigaction(SIGINT, &old_sig_action, NULL);

    virThreadJoin(&workerThread);

cleanup:
    virDomainFree(dom);
    VIR_FORCE_CLOSE(p[0]);
    VIR_FORCE_CLOSE(p[1]);
W
Wen Congyang 已提交
5771
    return functionReturn;
5772 5773
}

5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784
/*
 * "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")},
5785
    {"downtime", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum tolerable downtime (in milliseconds) for migration")},
5786 5787 5788
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
5789
static bool
5790 5791 5792
cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
5793
    long long downtime = 0;
E
Eric Blake 已提交
5794
    bool ret = false;
5795

5796
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
5797
        return false;
5798 5799

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

5802 5803
    if (vshCommandOptLongLong(cmd, "downtime", &downtime) < 0 ||
        downtime < 1) {
5804 5805 5806 5807 5808 5809 5810
        vshError(ctl, "%s", _("migrate: Invalid downtime"));
        goto done;
    }

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

E
Eric Blake 已提交
5811
    ret = true;
5812 5813 5814 5815 5816 5817

done:
    virDomainFree(dom);
    return ret;
}

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

5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900
/*
 * "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;
}

5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917
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))
5918
        goto cleanup;
5919 5920

    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
5921
        goto cleanup;
5922 5923

    if (vshCommandOptString(cmd, "path", &path) < 0)
5924
        goto cleanup;
5925

5926 5927
    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
        vshError(ctl, "%s", _("bandwidth must be a number"));
5928
        goto cleanup;
5929
    }
5930 5931 5932 5933 5934 5935 5936 5937 5938 5939

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

5940
cleanup:
5941 5942
    if (dom)
        virDomainFree(dom);
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 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026
    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")},
    {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Abort the active job on the speficied disk")},
    {"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;
}


6027 6028 6029
/*
 * "net-autostart" command
 */
6030
static const vshCmdInfo info_network_autostart[] = {
6031
    {"help", N_("autostart a network")},
6032
    {"desc",
6033
     N_("Configure a network to be automatically started at boot.")},
6034 6035 6036
    {NULL, NULL}
};

6037
static const vshCmdOptDef opts_network_autostart[] = {
6038 6039
    {"network",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
6040 6041 6042
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6043
static bool
6044
cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
6045 6046
{
    virNetworkPtr network;
6047
    const char *name;
6048 6049
    int autostart;

6050
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6051
        return false;
6052

J
Jim Meyering 已提交
6053
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
6054
        return false;
6055 6056 6057 6058

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

    if (virNetworkSetAutostart(network, autostart) < 0) {
6059
        if (autostart)
6060
            vshError(ctl, _("failed to mark network %s as autostarted"), name);
6061
        else
6062
            vshError(ctl, _("failed to unmark network %s as autostarted"), name);
6063
        virNetworkFree(network);
E
Eric Blake 已提交
6064
        return false;
6065 6066
    }

6067
    if (autostart)
6068
        vshPrint(ctl, _("Network %s marked as autostarted\n"), name);
6069
    else
6070
        vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name);
6071

L
Laine Stump 已提交
6072
    virNetworkFree(network);
E
Eric Blake 已提交
6073
    return true;
6074
}
K
Karel Zak 已提交
6075

6076 6077 6078
/*
 * "net-create" command
 */
6079
static const vshCmdInfo info_network_create[] = {
6080 6081
    {"help", N_("create a network from an XML file")},
    {"desc", N_("Create a network.")},
6082 6083 6084
    {NULL, NULL}
};

6085
static const vshCmdOptDef opts_network_create[] = {
6086
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
6087 6088 6089
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6090
static bool
6091
cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
6092 6093
{
    virNetworkPtr network;
6094
    const char *from = NULL;
E
Eric Blake 已提交
6095
    bool ret = true;
6096
    char *buffer;
6097

6098
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6099
        return false;
6100

6101
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
6102
        return false;
6103

6104
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
6105
        return false;
6106 6107

    network = virNetworkCreateXML(ctl->conn, buffer);
6108
    VIR_FREE(buffer);
6109

6110 6111 6112
    if (network != NULL) {
        vshPrint(ctl, _("Network %s created from %s\n"),
                 virNetworkGetName(network), from);
L
Laine Stump 已提交
6113
        virNetworkFree(network);
6114
    } else {
6115
        vshError(ctl, _("Failed to create network from %s"), from);
E
Eric Blake 已提交
6116
        ret = false;
6117 6118 6119 6120 6121 6122 6123 6124
    }
    return ret;
}


/*
 * "net-define" command
 */
6125
static const vshCmdInfo info_network_define[] = {
6126 6127
    {"help", N_("define (but don't start) a network from an XML file")},
    {"desc", N_("Define a network.")},
6128 6129 6130
    {NULL, NULL}
};

6131
static const vshCmdOptDef opts_network_define[] = {
6132
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
6133 6134 6135
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6136
static bool
6137
cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
6138 6139
{
    virNetworkPtr network;
6140
    const char *from = NULL;
E
Eric Blake 已提交
6141
    bool ret = true;
6142
    char *buffer;
6143

6144
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6145
        return false;
6146

6147
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
6148
        return false;
6149

6150
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
6151
        return false;
6152 6153

    network = virNetworkDefineXML(ctl->conn, buffer);
6154
    VIR_FREE(buffer);
6155

6156 6157 6158
    if (network != NULL) {
        vshPrint(ctl, _("Network %s defined from %s\n"),
                 virNetworkGetName(network), from);
L
Laine Stump 已提交
6159
        virNetworkFree(network);
6160
    } else {
6161
        vshError(ctl, _("Failed to define network from %s"), from);
E
Eric Blake 已提交
6162
        ret = false;
6163 6164 6165 6166 6167 6168 6169 6170
    }
    return ret;
}


/*
 * "net-destroy" command
 */
6171
static const vshCmdInfo info_network_destroy[] = {
6172 6173
    {"help", N_("destroy (stop) a network")},
    {"desc", N_("Forcefully stop a given network.")},
6174 6175 6176
    {NULL, NULL}
};

6177
static const vshCmdOptDef opts_network_destroy[] = {
6178
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
6179 6180 6181
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6182
static bool
6183
cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
6184 6185
{
    virNetworkPtr network;
E
Eric Blake 已提交
6186
    bool ret = true;
6187
    const char *name;
6188

6189
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6190
        return false;
6191

J
Jim Meyering 已提交
6192
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
6193
        return false;
6194 6195 6196 6197

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

6202
    virNetworkFree(network);
6203 6204 6205 6206 6207 6208 6209
    return ret;
}


/*
 * "net-dumpxml" command
 */
6210
static const vshCmdInfo info_network_dumpxml[] = {
6211 6212
    {"help", N_("network information in XML")},
    {"desc", N_("Output the network information as an XML dump to stdout.")},
6213 6214 6215
    {NULL, NULL}
};

6216
static const vshCmdOptDef opts_network_dumpxml[] = {
6217
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
6218 6219 6220
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6221
static bool
6222
cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
6223 6224
{
    virNetworkPtr network;
E
Eric Blake 已提交
6225
    bool ret = true;
6226 6227
    char *dump;

6228
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6229
        return false;
6230

J
Jim Meyering 已提交
6231
    if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
E
Eric Blake 已提交
6232
        return false;
6233 6234 6235

    dump = virNetworkGetXMLDesc(network, 0);
    if (dump != NULL) {
6236
        vshPrint(ctl, "%s", dump);
6237
        VIR_FREE(dump);
6238
    } else {
E
Eric Blake 已提交
6239
        ret = false;
6240 6241 6242 6243 6244 6245
    }

    virNetworkFree(network);
    return ret;
}

O
Osier Yang 已提交
6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259
/*
 * "net-info" command
 */
static const vshCmdInfo info_network_info[] = {
    {"help", N_("network information")},
    {"desc", "Returns basic information about the network"},
    {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 已提交
6260
static bool
O
Osier Yang 已提交
6261 6262 6263 6264 6265 6266 6267 6268 6269 6270
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 已提交
6271
        return false;
O
Osier Yang 已提交
6272 6273 6274

    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
                                           VSH_BYNAME)))
E
Eric Blake 已提交
6275
        return false;
O
Osier Yang 已提交
6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300

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

6301
    VIR_FREE(bridge);
O
Osier Yang 已提交
6302
    virNetworkFree(network);
E
Eric Blake 已提交
6303
    return true;
O
Osier Yang 已提交
6304
}
6305

6306 6307 6308 6309
/*
 * "iface-edit" command
 */
static const vshCmdInfo info_interface_edit[] = {
6310 6311
    {"help", N_("edit XML configuration for a physical host interface")},
    {"desc", N_("Edit the XML configuration for a physical host interface.")},
6312 6313 6314 6315
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_edit[] = {
6316
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
6317 6318 6319
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6320
static bool
6321 6322
cmdInterfaceEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
6323
    bool ret = false;
6324 6325 6326 6327 6328
    virInterfacePtr iface = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;
E
Eric Blake 已提交
6329
    unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
6330

6331
    if (!vshConnectionUsability(ctl, ctl->conn))
6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357
        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 已提交
6358
        ret = true;
6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370
        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)) {
6371 6372
        vshError(ctl, "%s",
                 _("ERROR: the XML configuration was changed by another user"));
6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384
        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 已提交
6385
    ret = true;
6386 6387 6388 6389 6390

cleanup:
    if (iface)
        virInterfaceFree (iface);

6391 6392 6393
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);
6394 6395 6396

    if (tmp) {
        unlink (tmp);
6397
        VIR_FREE(tmp);
6398 6399 6400 6401 6402
    }

    return ret;
}

6403 6404 6405
/*
 * "net-list" command
 */
6406
static const vshCmdInfo info_network_list[] = {
6407 6408
    {"help", N_("list networks")},
    {"desc", N_("Returns list of networks.")},
6409 6410 6411
    {NULL, NULL}
};

6412
static const vshCmdOptDef opts_network_list[] = {
6413 6414
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")},
6415 6416 6417
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6418
static bool
6419
cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
6420 6421 6422 6423 6424
{
    int inactive = vshCommandOptBool(cmd, "inactive");
    int all = vshCommandOptBool(cmd, "all");
    int active = !inactive || all ? 1 : 0;
    int maxactive = 0, maxinactive = 0, i;
6425
    char **activeNames = NULL, **inactiveNames = NULL;
6426 6427
    inactive |= all;

6428
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6429
        return false;
6430 6431

    if (active) {
6432 6433
        maxactive = virConnectNumOfNetworks(ctl->conn);
        if (maxactive < 0) {
6434
            vshError(ctl, "%s", _("Failed to list active networks"));
E
Eric Blake 已提交
6435
            return false;
6436
        }
6437
        if (maxactive) {
6438
            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
6439

6440
            if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
6441
                                                    maxactive)) < 0) {
6442
                vshError(ctl, "%s", _("Failed to list active networks"));
6443
                VIR_FREE(activeNames);
E
Eric Blake 已提交
6444
                return false;
6445
            }
6446

6447
            qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
6448
        }
6449 6450
    }
    if (inactive) {
6451 6452
        maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
        if (maxinactive < 0) {
6453
            vshError(ctl, "%s", _("Failed to list inactive networks"));
6454
            VIR_FREE(activeNames);
E
Eric Blake 已提交
6455
            return false;
6456
        }
6457 6458 6459
        if (maxinactive) {
            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);

6460 6461 6462
            if ((maxinactive =
                     virConnectListDefinedNetworks(ctl->conn, inactiveNames,
                                                   maxinactive)) < 0) {
6463
                vshError(ctl, "%s", _("Failed to list inactive networks"));
6464 6465
                VIR_FREE(activeNames);
                VIR_FREE(inactiveNames);
E
Eric Blake 已提交
6466
                return false;
6467
            }
6468

6469 6470
            qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
        }
6471
    }
6472 6473
    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
                  _("Autostart"));
6474
    vshPrintExtra(ctl, "-----------------------------------------\n");
6475 6476

    for (i = 0; i < maxactive; i++) {
6477 6478
        virNetworkPtr network =
            virNetworkLookupByName(ctl->conn, activeNames[i]);
6479 6480
        const char *autostartStr;
        int autostart = 0;
6481 6482 6483

        /* this kind of work with networks is not atomic operation */
        if (!network) {
6484
            VIR_FREE(activeNames[i]);
6485
            continue;
6486
        }
6487

6488 6489 6490
        if (virNetworkGetAutostart(network, &autostart) < 0)
            autostartStr = _("no autostart");
        else
6491
            autostartStr = autostart ? _("yes") : _("no");
6492 6493 6494 6495 6496

        vshPrint(ctl, "%-20s %-10s %-10s\n",
                 virNetworkGetName(network),
                 _("active"),
                 autostartStr);
6497
        virNetworkFree(network);
6498
        VIR_FREE(activeNames[i]);
6499 6500 6501
    }
    for (i = 0; i < maxinactive; i++) {
        virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
6502 6503
        const char *autostartStr;
        int autostart = 0;
6504 6505 6506

        /* this kind of work with networks is not atomic operation */
        if (!network) {
6507
            VIR_FREE(inactiveNames[i]);
6508
            continue;
6509
        }
6510

6511 6512 6513
        if (virNetworkGetAutostart(network, &autostart) < 0)
            autostartStr = _("no autostart");
        else
6514
            autostartStr = autostart ? _("yes") : _("no");
6515

6516
        vshPrint(ctl, "%-20s %-10s %-10s\n",
6517 6518 6519
                 inactiveNames[i],
                 _("inactive"),
                 autostartStr);
6520 6521

        virNetworkFree(network);
6522
        VIR_FREE(inactiveNames[i]);
6523
    }
6524 6525
    VIR_FREE(activeNames);
    VIR_FREE(inactiveNames);
E
Eric Blake 已提交
6526
    return true;
6527 6528 6529 6530 6531 6532
}


/*
 * "net-name" command
 */
6533
static const vshCmdInfo info_network_name[] = {
6534
    {"help", N_("convert a network UUID to network name")},
6535
    {"desc", ""},
6536 6537 6538
    {NULL, NULL}
};

6539
static const vshCmdOptDef opts_network_name[] = {
6540
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")},
6541 6542 6543
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6544
static bool
6545
cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
6546 6547 6548
{
    virNetworkPtr network;

6549
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6550
        return false;
J
Jim Meyering 已提交
6551
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
6552
                                           VSH_BYUUID)))
E
Eric Blake 已提交
6553
        return false;
6554 6555 6556

    vshPrint(ctl, "%s\n", virNetworkGetName(network));
    virNetworkFree(network);
E
Eric Blake 已提交
6557
    return true;
6558 6559 6560 6561 6562 6563
}


/*
 * "net-start" command
 */
6564
static const vshCmdInfo info_network_start[] = {
6565 6566
    {"help", N_("start a (previously defined) inactive network")},
    {"desc", N_("Start a network.")},
6567 6568 6569
    {NULL, NULL}
};

6570
static const vshCmdOptDef opts_network_start[] = {
6571
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive network")},
6572 6573 6574
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6575
static bool
6576
cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
6577 6578
{
    virNetworkPtr network;
E
Eric Blake 已提交
6579
    bool ret = true;
6580

6581
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6582
        return false;
6583

J
Jim Meyering 已提交
6584
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL, VSH_BYNAME)))
E
Eric Blake 已提交
6585
         return false;
6586 6587 6588

    if (virNetworkCreate(network) == 0) {
        vshPrint(ctl, _("Network %s started\n"),
6589
                 virNetworkGetName(network));
6590
    } else {
6591
        vshError(ctl, _("Failed to start network %s"),
6592
                 virNetworkGetName(network));
E
Eric Blake 已提交
6593
        ret = false;
6594
    }
L
Laine Stump 已提交
6595
    virNetworkFree(network);
6596 6597 6598 6599 6600 6601 6602
    return ret;
}


/*
 * "net-undefine" command
 */
6603
static const vshCmdInfo info_network_undefine[] = {
6604 6605
    {"help", N_("undefine an inactive network")},
    {"desc", N_("Undefine the configuration for an inactive network.")},
6606 6607 6608
    {NULL, NULL}
};

6609
static const vshCmdOptDef opts_network_undefine[] = {
6610
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
6611 6612 6613
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6614
static bool
6615
cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
6616 6617
{
    virNetworkPtr network;
E
Eric Blake 已提交
6618
    bool ret = true;
6619
    const char *name;
6620

6621
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6622
        return false;
6623

J
Jim Meyering 已提交
6624
    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
E
Eric Blake 已提交
6625
        return false;
6626 6627 6628 6629

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

L
Laine Stump 已提交
6634
    virNetworkFree(network);
6635 6636 6637 6638 6639 6640 6641
    return ret;
}


/*
 * "net-uuid" command
 */
6642
static const vshCmdInfo info_network_uuid[] = {
6643
    {"help", N_("convert a network name to network UUID")},
6644
    {"desc", ""},
6645 6646 6647
    {NULL, NULL}
};

6648
static const vshCmdOptDef opts_network_uuid[] = {
6649
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
6650 6651 6652
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6653
static bool
6654
cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
6655 6656 6657 6658
{
    virNetworkPtr network;
    char uuid[VIR_UUID_STRING_BUFLEN];

6659
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6660
        return false;
6661

J
Jim Meyering 已提交
6662
    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
6663
                                           VSH_BYNAME)))
E
Eric Blake 已提交
6664
        return false;
6665 6666 6667 6668

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

L
Laine Stump 已提交
6671
    virNetworkFree(network);
E
Eric Blake 已提交
6672
    return true;
6673 6674 6675
}


6676 6677 6678 6679 6680
/**************************************************************************/
/*
 * "iface-list" command
 */
static const vshCmdInfo info_interface_list[] = {
6681 6682
    {"help", N_("list physical host interfaces")},
    {"desc", N_("Returns list of physical host interfaces.")},
6683 6684 6685 6686
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_list[] = {
6687 6688
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive interfaces")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")},
6689 6690
    {NULL, 0, 0, NULL}
};
E
Eric Blake 已提交
6691
static bool
6692 6693 6694 6695 6696 6697 6698 6699 6700
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;

6701
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6702
        return false;
6703 6704 6705 6706

    if (active) {
        maxactive = virConnectNumOfInterfaces(ctl->conn);
        if (maxactive < 0) {
6707
            vshError(ctl, "%s", _("Failed to list active interfaces"));
E
Eric Blake 已提交
6708
            return false;
6709 6710 6711 6712 6713 6714
        }
        if (maxactive) {
            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);

            if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames,
                                                    maxactive)) < 0) {
6715
                vshError(ctl, "%s", _("Failed to list active interfaces"));
6716
                VIR_FREE(activeNames);
E
Eric Blake 已提交
6717
                return false;
6718 6719 6720 6721 6722 6723 6724 6725
            }

            qsort(&activeNames[0], maxactive, sizeof(char *), namesorter);
        }
    }
    if (inactive) {
        maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn);
        if (maxinactive < 0) {
6726
            vshError(ctl, "%s", _("Failed to list inactive interfaces"));
6727
            VIR_FREE(activeNames);
E
Eric Blake 已提交
6728
            return false;
6729 6730 6731 6732
        }
        if (maxinactive) {
            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);

6733 6734 6735
            if ((maxinactive =
                     virConnectListDefinedInterfaces(ctl->conn, inactiveNames,
                                                     maxinactive)) < 0) {
6736
                vshError(ctl, "%s", _("Failed to list inactive interfaces"));
6737 6738
                VIR_FREE(activeNames);
                VIR_FREE(inactiveNames);
E
Eric Blake 已提交
6739
                return false;
6740 6741 6742 6743 6744
            }

            qsort(&inactiveNames[0], maxinactive, sizeof(char*), namesorter);
        }
    }
6745 6746
    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
                  _("MAC Address"));
6747 6748 6749
    vshPrintExtra(ctl, "--------------------------------------------\n");

    for (i = 0; i < maxactive; i++) {
6750 6751
        virInterfacePtr iface =
            virInterfaceLookupByName(ctl->conn, activeNames[i]);
6752 6753 6754

        /* this kind of work with interfaces is not atomic */
        if (!iface) {
6755
            VIR_FREE(activeNames[i]);
6756 6757 6758 6759 6760 6761 6762 6763
            continue;
        }

        vshPrint(ctl, "%-20s %-10s %s\n",
                 virInterfaceGetName(iface),
                 _("active"),
                 virInterfaceGetMACString(iface));
        virInterfaceFree(iface);
6764
        VIR_FREE(activeNames[i]);
6765 6766
    }
    for (i = 0; i < maxinactive; i++) {
6767 6768
        virInterfacePtr iface =
            virInterfaceLookupByName(ctl->conn, inactiveNames[i]);
6769 6770 6771

        /* this kind of work with interfaces is not atomic */
        if (!iface) {
6772
            VIR_FREE(inactiveNames[i]);
6773 6774 6775 6776 6777 6778 6779 6780
            continue;
        }

        vshPrint(ctl, "%-20s %-10s %s\n",
                 virInterfaceGetName(iface),
                 _("inactive"),
                 virInterfaceGetMACString(iface));
        virInterfaceFree(iface);
6781
        VIR_FREE(inactiveNames[i]);
6782
    }
6783 6784
    VIR_FREE(activeNames);
    VIR_FREE(inactiveNames);
E
Eric Blake 已提交
6785
    return true;
6786 6787 6788 6789 6790 6791 6792

}

/*
 * "iface-name" command
 */
static const vshCmdInfo info_interface_name[] = {
6793
    {"help", N_("convert an interface MAC address to interface name")},
6794 6795 6796 6797 6798
    {"desc", ""},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_name[] = {
6799
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface mac")},
6800 6801 6802
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6803
static bool
6804 6805 6806 6807
cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;

6808
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6809
        return false;
6810 6811
    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL,
                                           VSH_BYMAC)))
E
Eric Blake 已提交
6812
        return false;
6813 6814 6815

    vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
    virInterfaceFree(iface);
E
Eric Blake 已提交
6816
    return true;
6817 6818 6819 6820 6821 6822
}

/*
 * "iface-mac" command
 */
static const vshCmdInfo info_interface_mac[] = {
6823
    {"help", N_("convert an interface name to interface MAC address")},
6824 6825 6826 6827 6828
    {"desc", ""},
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_mac[] = {
6829
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
6830 6831 6832
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6833
static bool
6834 6835 6836 6837
cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;

6838
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6839
        return false;
6840 6841
    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL,
                                           VSH_BYNAME)))
E
Eric Blake 已提交
6842
        return false;
6843 6844 6845

    vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
    virInterfaceFree(iface);
E
Eric Blake 已提交
6846
    return true;
6847 6848 6849 6850 6851 6852
}

/*
 * "iface-dumpxml" command
 */
static const vshCmdInfo info_interface_dumpxml[] = {
6853 6854
    {"help", N_("interface information in XML")},
    {"desc", N_("Output the physical host interface information as an XML dump to stdout.")},
6855 6856 6857 6858
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_dumpxml[] = {
6859 6860
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
6861 6862 6863
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6864
static bool
6865 6866 6867
cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
6868
    bool ret = true;
6869
    char *dump;
E
Eric Blake 已提交
6870
    unsigned int flags = 0;
6871 6872 6873 6874
    int inactive = vshCommandOptBool(cmd, "inactive");

    if (inactive)
        flags |= VIR_INTERFACE_XML_INACTIVE;
6875

6876
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6877
        return false;
6878 6879

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

6882
    dump = virInterfaceGetXMLDesc(iface, flags);
6883
    if (dump != NULL) {
6884
        vshPrint(ctl, "%s", dump);
6885
        VIR_FREE(dump);
6886
    } else {
E
Eric Blake 已提交
6887
        ret = false;
6888 6889 6890 6891 6892 6893 6894 6895 6896 6897
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-define" command
 */
static const vshCmdInfo info_interface_define[] = {
6898 6899
    {"help", N_("define (but don't start) a physical host interface from an XML file")},
    {"desc", N_("Define a physical host interface.")},
6900 6901 6902 6903
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_define[] = {
6904
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML interface description")},
6905 6906 6907
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6908
static bool
6909 6910
cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
{
6911
    virInterfacePtr iface;
6912
    const char *from = NULL;
E
Eric Blake 已提交
6913
    bool ret = true;
6914 6915
    char *buffer;

6916
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6917
        return false;
6918

6919
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
6920
        return false;
6921 6922

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

6925
    iface = virInterfaceDefineXML(ctl->conn, buffer, 0);
6926
    VIR_FREE(buffer);
6927

6928
    if (iface != NULL) {
6929
        vshPrint(ctl, _("Interface %s defined from %s\n"),
6930
                 virInterfaceGetName(iface), from);
L
Laine Stump 已提交
6931
        virInterfaceFree (iface);
6932
    } else {
6933
        vshError(ctl, _("Failed to define interface from %s"), from);
E
Eric Blake 已提交
6934
        ret = false;
6935 6936 6937 6938 6939 6940 6941 6942
    }
    return ret;
}

/*
 * "iface-undefine" command
 */
static const vshCmdInfo info_interface_undefine[] = {
6943 6944
    {"help", N_("undefine a physical host interface (remove it from configuration)")},
    {"desc", N_("undefine an interface.")},
6945 6946 6947 6948
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_undefine[] = {
6949
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
6950 6951 6952
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6953
static bool
6954 6955 6956
cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
6957
    bool ret = true;
6958
    const char *name;
6959

6960
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6961
        return false;
6962 6963

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
6964
        return false;
6965 6966 6967 6968

    if (virInterfaceUndefine(iface) == 0) {
        vshPrint(ctl, _("Interface %s undefined\n"), name);
    } else {
6969
        vshError(ctl, _("Failed to undefine interface %s"), name);
E
Eric Blake 已提交
6970
        ret = false;
6971 6972 6973 6974 6975 6976 6977 6978 6979 6980
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-start" command
 */
static const vshCmdInfo info_interface_start[] = {
6981 6982
    {"help", N_("start a physical host interface (enable it / \"if-up\")")},
    {"desc", N_("start a physical host interface.")},
6983 6984 6985 6986
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_start[] = {
6987
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
6988 6989 6990
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
6991
static bool
6992 6993 6994
cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
6995
    bool ret = true;
6996
    const char *name;
6997

6998
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
6999
        return false;
7000 7001

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
7002
        return false;
7003 7004 7005 7006

    if (virInterfaceCreate(iface, 0) == 0) {
        vshPrint(ctl, _("Interface %s started\n"), name);
    } else {
7007
        vshError(ctl, _("Failed to start interface %s"), name);
E
Eric Blake 已提交
7008
        ret = false;
7009 7010 7011 7012 7013 7014 7015 7016 7017 7018
    }

    virInterfaceFree(iface);
    return ret;
}

/*
 * "iface-destroy" command
 */
static const vshCmdInfo info_interface_destroy[] = {
7019
    {"help", N_("destroy a physical host interface (disable it / \"if-down\")")},
7020
    {"desc", N_("forcefully stop a physical host interface.")},
7021 7022 7023 7024
    {NULL, NULL}
};

static const vshCmdOptDef opts_interface_destroy[] = {
7025
    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
7026 7027 7028
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7029
static bool
7030 7031 7032
cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
{
    virInterfacePtr iface;
E
Eric Blake 已提交
7033
    bool ret = true;
7034
    const char *name;
7035

7036
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7037
        return false;
7038 7039

    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
E
Eric Blake 已提交
7040
        return false;
7041 7042 7043 7044

    if (virInterfaceDestroy(iface, 0) == 0) {
        vshPrint(ctl, _("Interface %s destroyed\n"), name);
    } else {
7045
        vshError(ctl, _("Failed to destroy interface %s"), name);
E
Eric Blake 已提交
7046
        ret = false;
7047 7048 7049 7050 7051 7052
    }

    virInterfaceFree(iface);
    return ret;
}

7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137
/*
 * "iface-begin" command
 */
static const vshCmdInfo info_interface_begin[] = {
    {"help", N_("create a snapshot of current interfaces settings, "
                "which can be later commited (iface-commit) or "
                "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;
}
7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152

/*
 * "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 已提交
7153
static bool
7154 7155 7156
cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
7157
    const char *from = NULL;
E
Eric Blake 已提交
7158
    bool ret = true;
7159 7160
    char *buffer;

7161
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7162
        return false;
7163

7164
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
7165
        return false;
7166 7167

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
7168
        return false;
7169 7170 7171 7172 7173 7174 7175 7176 7177 7178

    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 已提交
7179
        ret = false;
7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198
    }
    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 已提交
7199
static bool
7200 7201 7202
cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
E
Eric Blake 已提交
7203
    bool ret = true;
7204
    const char *name;
7205

7206
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7207
        return false;
7208 7209

    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name)))
E
Eric Blake 已提交
7210
        return false;
7211 7212 7213 7214 7215

    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 已提交
7216
        ret = false;
7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237
    }

    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 已提交
7238
static bool
7239 7240 7241
cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virNWFilterPtr nwfilter;
E
Eric Blake 已提交
7242
    bool ret = true;
7243 7244
    char *dump;

7245
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7246
        return false;
7247 7248

    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL)))
E
Eric Blake 已提交
7249
        return false;
7250 7251 7252

    dump = virNWFilterGetXMLDesc(nwfilter, 0);
    if (dump != NULL) {
7253
        vshPrint(ctl, "%s", dump);
7254 7255
        VIR_FREE(dump);
    } else {
E
Eric Blake 已提交
7256
        ret = false;
7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275
    }

    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 已提交
7276
static bool
7277 7278 7279 7280 7281 7282
cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    int numfilters, i;
    char **names;
    char uuid[VIR_UUID_STRING_BUFLEN];

7283
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7284
        return false;
7285 7286 7287 7288

    numfilters = virConnectNumOfNWFilters(ctl->conn);
    if (numfilters < 0) {
        vshError(ctl, "%s", _("Failed to list network filters"));
E
Eric Blake 已提交
7289
        return false;
7290 7291 7292 7293 7294 7295 7296 7297
    }

    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 已提交
7298
        return false;
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
    }

    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 已提交
7326
    return true;
7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343
}


/*
 * "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 已提交
7344
static bool
7345 7346
cmdNWFilterEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
7347
    bool ret = false;
7348 7349 7350 7351 7352 7353
    virNWFilterPtr nwfilter = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;

7354
    if (!vshConnectionUsability(ctl, ctl->conn))
7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380
        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 已提交
7381
        ret = true;
7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407
        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 已提交
7408
    ret = true;
7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426

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


7427
/**************************************************************************/
7428
/*
7429
 * "pool-autostart" command
7430
 */
7431
static const vshCmdInfo info_pool_autostart[] = {
7432
    {"help", N_("autostart a pool")},
7433
    {"desc",
7434
     N_("Configure a pool to be automatically started at boot.")},
7435
    {NULL, NULL}
7436 7437
};

7438
static const vshCmdOptDef opts_pool_autostart[] = {
7439 7440
    {"pool",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
7441 7442
    {NULL, 0, 0, NULL}
};
7443

E
Eric Blake 已提交
7444
static bool
7445
cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd)
7446
{
7447
    virStoragePoolPtr pool;
7448
    const char *name;
7449
    int autostart;
7450

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

7454
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
7455
        return false;
7456

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

7459 7460
    if (virStoragePoolSetAutostart(pool, autostart) < 0) {
        if (autostart)
7461
            vshError(ctl, _("failed to mark pool %s as autostarted"), name);
7462
        else
7463
            vshError(ctl, _("failed to unmark pool %s as autostarted"), name);
7464
        virStoragePoolFree(pool);
E
Eric Blake 已提交
7465
        return false;
7466 7467
    }

7468
    if (autostart)
7469
        vshPrint(ctl, _("Pool %s marked as autostarted\n"), name);
7470
    else
7471
        vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name);
7472

L
Laine Stump 已提交
7473
    virStoragePoolFree(pool);
E
Eric Blake 已提交
7474
    return true;
7475 7476
}

7477
/*
7478
 * "pool-create" command
7479
 */
7480
static const vshCmdInfo info_pool_create[] = {
7481 7482
    {"help", N_("create a pool from an XML file")},
    {"desc", N_("Create a pool.")},
7483 7484 7485
    {NULL, NULL}
};

7486
static const vshCmdOptDef opts_pool_create[] = {
J
Jim Meyering 已提交
7487
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
7488
     N_("file containing an XML pool description")},
7489 7490 7491
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7492
static bool
7493
cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
7494
{
7495
    virStoragePoolPtr pool;
7496
    const char *from = NULL;
E
Eric Blake 已提交
7497
    bool ret = true;
7498
    char *buffer;
7499

7500
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7501
        return false;
7502

7503
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
7504
        return false;
7505

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

7509
    pool = virStoragePoolCreateXML(ctl->conn, buffer, 0);
7510
    VIR_FREE(buffer);
7511 7512 7513 7514

    if (pool != NULL) {
        vshPrint(ctl, _("Pool %s created from %s\n"),
                 virStoragePoolGetName(pool), from);
L
Laine Stump 已提交
7515
        virStoragePoolFree(pool);
7516
    } else {
7517
        vshError(ctl, _("Failed to create pool from %s"), from);
E
Eric Blake 已提交
7518
        ret = false;
7519 7520
    }
    return ret;
7521 7522
}

7523

7524 7525 7526 7527
/*
 * "nodedev-create" command
 */
static const vshCmdInfo info_node_device_create[] = {
7528
    {"help", N_("create a device defined "
7529
                          "by an XML file on the node")},
7530
    {"desc", N_("Create a device on the node.  Note that this "
7531 7532 7533 7534 7535 7536 7537
                          "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,
7538
     N_("file containing an XML description of the device")},
7539 7540 7541
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7542
static bool
7543 7544 7545
cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
{
    virNodeDevicePtr dev = NULL;
7546
    const char *from = NULL;
E
Eric Blake 已提交
7547
    bool ret = true;
7548 7549
    char *buffer;

7550
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7551
        return false;
7552

7553
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
7554
        return false;
7555

7556
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
E
Eric Blake 已提交
7557
        return false;
7558 7559

    dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
7560
    VIR_FREE(buffer);
7561 7562 7563 7564

    if (dev != NULL) {
        vshPrint(ctl, _("Node device %s created from %s\n"),
                 virNodeDeviceGetName(dev), from);
L
Laine Stump 已提交
7565
        virNodeDeviceFree(dev);
7566
    } else {
7567
        vshError(ctl, _("Failed to create node device from %s"), from);
E
Eric Blake 已提交
7568
        ret = false;
7569 7570 7571 7572 7573 7574 7575 7576 7577 7578
    }

    return ret;
}


/*
 * "nodedev-destroy" command
 */
static const vshCmdInfo info_node_device_destroy[] = {
7579
    {"help", N_("destroy (stop) a device on the node")},
7580
    {"desc", N_("Destroy a device on the node.  Note that this "
7581
                "command destroys devices on the physical host")},
7582 7583 7584 7585 7586
    {NULL, NULL}
};

static const vshCmdOptDef opts_node_device_destroy[] = {
    {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
7587
     N_("name of the device to be destroyed")},
7588 7589 7590
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7591
static bool
7592 7593 7594
cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
{
    virNodeDevicePtr dev = NULL;
E
Eric Blake 已提交
7595
    bool ret = true;
7596
    const char *name = NULL;
7597

7598
    if (!vshConnectionUsability(ctl, ctl->conn)) {
E
Eric Blake 已提交
7599
        return false;
7600 7601
    }

7602
    if (vshCommandOptString(cmd, "name", &name) <= 0)
E
Eric Blake 已提交
7603
        return false;
7604 7605 7606 7607 7608 7609

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

    if (virNodeDeviceDestroy(dev) == 0) {
        vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
    } else {
7610
        vshError(ctl, _("Failed to destroy node device '%s'"), name);
E
Eric Blake 已提交
7611
        ret = false;
7612 7613 7614 7615 7616 7617 7618
    }

    virNodeDeviceFree(dev);
    return ret;
}


7619
/*
7620
 * XML Building helper for pool-define-as and pool-create-as
7621
 */
7622
static const vshCmdOptDef opts_pool_X_as[] = {
7623 7624 7625 7626 7627 7628 7629 7630
    {"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")},
7631
    {"source-format", VSH_OT_STRING, 0, N_("format for underlying storage")},
7632 7633 7634
    {NULL, 0, 0, NULL}
};

7635
static int buildPoolXML(const vshCmd *cmd, const char **retname, char **xml) {
7636

7637 7638
    const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL,
               *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL;
7639
    virBuffer buf = VIR_BUFFER_INITIALIZER;
7640

7641
    if (vshCommandOptString(cmd, "name", &name) <= 0)
7642
        goto cleanup;
7643
    if (vshCommandOptString(cmd, "type", &type) <= 0)
7644 7645
        goto cleanup;

7646 7647 7648 7649 7650
    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 ||
7651 7652
        vshCommandOptString(cmd, "target", &target) < 0) {
        vshError(NULL, "%s", _("missing argument"));
7653
        goto cleanup;
7654
    }
7655

7656 7657
    virBufferAsprintf(&buf, "<pool type='%s'>\n", type);
    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
7658
    if (srcHost || srcPath || srcDev || srcFormat || srcName) {
7659
        virBufferAddLit(&buf, "  <source>\n");
7660

7661
        if (srcHost)
7662
            virBufferAsprintf(&buf, "    <host name='%s'/>\n", srcHost);
7663
        if (srcPath)
7664
            virBufferAsprintf(&buf, "    <dir path='%s'/>\n", srcPath);
7665
        if (srcDev)
7666
            virBufferAsprintf(&buf, "    <device path='%s'/>\n", srcDev);
7667
        if (srcFormat)
7668
            virBufferAsprintf(&buf, "    <format type='%s'/>\n", srcFormat);
7669
        if (srcName)
7670
            virBufferAsprintf(&buf, "    <name>%s</name>\n", srcName);
7671 7672

        virBufferAddLit(&buf, "  </source>\n");
7673 7674
    }
    if (target) {
7675
        virBufferAddLit(&buf, "  <target>\n");
7676
        virBufferAsprintf(&buf, "    <path>%s</path>\n", target);
7677
        virBufferAddLit(&buf, "  </target>\n");
7678
    }
7679 7680 7681 7682
    virBufferAddLit(&buf, "</pool>\n");

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
7683
        return false;
7684
    }
7685 7686 7687

    *xml = virBufferContentAndReset(&buf);
    *retname = name;
E
Eric Blake 已提交
7688
    return true;
7689 7690

cleanup:
7691
    virBufferFreeAndReset(&buf);
E
Eric Blake 已提交
7692
    return false;
7693 7694 7695 7696 7697 7698
}

/*
 * "pool-create-as" command
 */
static const vshCmdInfo info_pool_create_as[] = {
7699 7700
    {"help", N_("create a pool from a set of args")},
    {"desc", N_("Create a pool.")},
7701 7702 7703
    {NULL, NULL}
};

E
Eric Blake 已提交
7704
static bool
7705 7706 7707
cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool;
7708 7709
    const char *name;
    char *xml;
7710
    int printXML = vshCommandOptBool(cmd, "print-xml");
7711

7712
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7713
        return false;
7714 7715

    if (!buildPoolXML(cmd, &name, &xml))
E
Eric Blake 已提交
7716
        return false;
7717

7718
    if (printXML) {
7719
        vshPrint(ctl, "%s", xml);
7720
        VIR_FREE(xml);
7721
    } else {
7722
        pool = virStoragePoolCreateXML(ctl->conn, xml, 0);
7723
        VIR_FREE(xml);
7724

7725 7726 7727 7728
        if (pool != NULL) {
            vshPrint(ctl, _("Pool %s created\n"), name);
            virStoragePoolFree(pool);
        } else {
7729
            vshError(ctl, _("Failed to create pool %s"), name);
E
Eric Blake 已提交
7730
            return false;
7731 7732
        }
    }
E
Eric Blake 已提交
7733
    return true;
7734 7735
}

7736

7737
/*
7738
 * "pool-define" command
7739
 */
7740
static const vshCmdInfo info_pool_define[] = {
7741 7742
    {"help", N_("define (but don't start) a pool from an XML file")},
    {"desc", N_("Define a pool.")},
7743 7744 7745
    {NULL, NULL}
};

7746
static const vshCmdOptDef opts_pool_define[] = {
7747
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML pool description")},
7748 7749 7750
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7751
static bool
7752
cmdPoolDefine(vshControl *ctl, const vshCmd *cmd)
7753
{
7754
    virStoragePoolPtr pool;
7755
    const char *from = NULL;
E
Eric Blake 已提交
7756
    bool ret = true;
7757
    char *buffer;
7758

7759
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7760
        return false;
7761

7762
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
7763
        return false;
7764

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

7768
    pool = virStoragePoolDefineXML(ctl->conn, buffer, 0);
7769
    VIR_FREE(buffer);
7770 7771 7772 7773

    if (pool != NULL) {
        vshPrint(ctl, _("Pool %s defined from %s\n"),
                 virStoragePoolGetName(pool), from);
L
Laine Stump 已提交
7774
        virStoragePoolFree(pool);
7775
    } else {
7776
        vshError(ctl, _("Failed to define pool from %s"), from);
E
Eric Blake 已提交
7777
        ret = false;
7778 7779
    }
    return ret;
7780 7781
}

7782

7783 7784 7785
/*
 * "pool-define-as" command
 */
7786
static const vshCmdInfo info_pool_define_as[] = {
7787 7788
    {"help", N_("define a pool from a set of args")},
    {"desc", N_("Define a pool.")},
7789 7790 7791
    {NULL, NULL}
};

E
Eric Blake 已提交
7792
static bool
7793
cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd)
7794 7795
{
    virStoragePoolPtr pool;
7796 7797
    const char *name;
    char *xml;
7798
    int printXML = vshCommandOptBool(cmd, "print-xml");
7799

7800
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7801
        return false;
7802

7803
    if (!buildPoolXML(cmd, &name, &xml))
E
Eric Blake 已提交
7804
        return false;
7805

7806
    if (printXML) {
7807
        vshPrint(ctl, "%s", xml);
7808
        VIR_FREE(xml);
7809
    } else {
7810
        pool = virStoragePoolDefineXML(ctl->conn, xml, 0);
7811
        VIR_FREE(xml);
7812

7813 7814 7815 7816
        if (pool != NULL) {
            vshPrint(ctl, _("Pool %s defined\n"), name);
            virStoragePoolFree(pool);
        } else {
7817
            vshError(ctl, _("Failed to define pool %s"), name);
E
Eric Blake 已提交
7818
            return false;
7819 7820
        }
    }
E
Eric Blake 已提交
7821
    return true;
7822 7823 7824
}


7825
/*
7826
 * "pool-build" command
7827
 */
7828
static const vshCmdInfo info_pool_build[] = {
7829 7830
    {"help", N_("build a pool")},
    {"desc", N_("Build a given pool.")},
7831 7832 7833
    {NULL, NULL}
};

7834
static const vshCmdOptDef opts_pool_build[] = {
7835
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
7836 7837
    {"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")},
7838 7839 7840
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7841
static bool
7842
cmdPoolBuild(vshControl *ctl, const vshCmd *cmd)
7843
{
7844
    virStoragePoolPtr pool;
E
Eric Blake 已提交
7845
    bool ret = true;
7846
    const char *name;
7847
    unsigned int flags = 0;
7848

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

7852
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
7853
        return false;
7854

7855 7856 7857 7858 7859 7860 7861 7862 7863
    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) {
7864
        vshPrint(ctl, _("Pool %s built\n"), name);
7865
    } else {
7866
        vshError(ctl, _("Failed to build pool %s"), name);
E
Eric Blake 已提交
7867
        ret = false;
7868 7869
    }

7870 7871
    virStoragePoolFree(pool);

7872 7873 7874
    return ret;
}

7875
/*
7876
 * "pool-destroy" command
7877
 */
7878
static const vshCmdInfo info_pool_destroy[] = {
7879 7880 7881
    {"help", N_("destroy (stop) a pool")},
    {"desc",
     N_("Forcefully stop a given pool. Raw data in the pool is untouched")},
7882 7883 7884
    {NULL, NULL}
};

7885
static const vshCmdOptDef opts_pool_destroy[] = {
7886
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
7887 7888 7889
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7890
static bool
7891
cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd)
7892
{
7893
    virStoragePoolPtr pool;
E
Eric Blake 已提交
7894
    bool ret = true;
7895
    const char *name;
7896

7897
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7898
        return false;
7899

7900
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
7901
        return false;
7902

7903 7904 7905
    if (virStoragePoolDestroy(pool) == 0) {
        vshPrint(ctl, _("Pool %s destroyed\n"), name);
    } else {
7906
        vshError(ctl, _("Failed to destroy pool %s"), name);
E
Eric Blake 已提交
7907
        ret = false;
7908 7909
    }

7910
    virStoragePoolFree(pool);
7911 7912 7913
    return ret;
}

7914

7915
/*
7916 7917
 * "pool-delete" command
 */
7918
static const vshCmdInfo info_pool_delete[] = {
7919 7920
    {"help", N_("delete a pool")},
    {"desc", N_("Delete a given pool.")},
7921 7922 7923
    {NULL, NULL}
};

7924
static const vshCmdOptDef opts_pool_delete[] = {
7925
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
7926 7927 7928
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7929
static bool
7930
cmdPoolDelete(vshControl *ctl, const vshCmd *cmd)
7931 7932
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
7933
    bool ret = true;
7934
    const char *name;
7935

7936
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7937
        return false;
7938 7939

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
7940
        return false;
7941 7942

    if (virStoragePoolDelete(pool, 0) == 0) {
D
Daniel Veillard 已提交
7943
        vshPrint(ctl, _("Pool %s deleted\n"), name);
7944
    } else {
7945
        vshError(ctl, _("Failed to delete pool %s"), name);
E
Eric Blake 已提交
7946
        ret = false;
7947 7948
    }

L
Laine Stump 已提交
7949
    virStoragePoolFree(pool);
7950 7951 7952 7953 7954 7955 7956
    return ret;
}


/*
 * "pool-refresh" command
 */
7957
static const vshCmdInfo info_pool_refresh[] = {
7958 7959
    {"help", N_("refresh a pool")},
    {"desc", N_("Refresh a given pool.")},
7960 7961 7962
    {NULL, NULL}
};

7963
static const vshCmdOptDef opts_pool_refresh[] = {
7964
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
7965 7966 7967
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
7968
static bool
7969
cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd)
7970 7971
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
7972
    bool ret = true;
7973
    const char *name;
7974

7975
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
7976
        return false;
7977 7978

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
7979
        return false;
7980 7981 7982 7983

    if (virStoragePoolRefresh(pool, 0) == 0) {
        vshPrint(ctl, _("Pool %s refreshed\n"), name);
    } else {
7984
        vshError(ctl, _("Failed to refresh pool %s"), name);
E
Eric Blake 已提交
7985
        ret = false;
7986 7987 7988 7989 7990 7991 7992 7993 7994 7995
    }
    virStoragePoolFree(pool);

    return ret;
}


/*
 * "pool-dumpxml" command
 */
7996
static const vshCmdInfo info_pool_dumpxml[] = {
7997 7998
    {"help", N_("pool information in XML")},
    {"desc", N_("Output the pool information as an XML dump to stdout.")},
7999 8000 8001
    {NULL, NULL}
};

8002
static const vshCmdOptDef opts_pool_dumpxml[] = {
8003
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
8004 8005 8006
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8007
static bool
8008
cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
8009 8010
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
8011
    bool ret = true;
8012 8013
    char *dump;

8014
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8015
        return false;
8016 8017

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
8018
        return false;
8019 8020 8021

    dump = virStoragePoolGetXMLDesc(pool, 0);
    if (dump != NULL) {
8022
        vshPrint(ctl, "%s", dump);
8023
        VIR_FREE(dump);
8024
    } else {
E
Eric Blake 已提交
8025
        ret = false;
8026 8027 8028 8029 8030 8031 8032 8033 8034 8035
    }

    virStoragePoolFree(pool);
    return ret;
}


/*
 * "pool-list" command
 */
8036
static const vshCmdInfo info_pool_list[] = {
8037 8038
    {"help", N_("list pools")},
    {"desc", N_("Returns list of pools.")},
8039 8040 8041
    {NULL, NULL}
};

8042
static const vshCmdOptDef opts_pool_list[] = {
8043 8044
    {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")},
    {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")},
8045
    {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")},
8046 8047 8048
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8049
static bool
8050
cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
8051
{
8052 8053
    virStoragePoolInfo info;
    char **poolNames = NULL;
W
Wen Congyang 已提交
8054 8055
    int i, ret;
    bool functionReturn;
8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071
    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 */
8072
    int all = vshCommandOptBool(cmd, "all");
8073 8074
    int details = vshCommandOptBool(cmd, "details");
    int inactive = vshCommandOptBool(cmd, "inactive");
8075 8076 8077
    int active = !inactive || all ? 1 : 0;
    inactive |= all;

8078
    /* Check the connection to libvirtd daemon is still working */
8079
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8080
        return false;
8081

8082
    /* Retrieve the number of active storage pools */
8083
    if (active) {
8084 8085
        numActivePools = virConnectNumOfStoragePools(ctl->conn);
        if (numActivePools < 0) {
8086
            vshError(ctl, "%s", _("Failed to list active pools"));
E
Eric Blake 已提交
8087
            return false;
8088 8089
        }
    }
8090 8091

    /* Retrieve the number of inactive storage pools */
8092
    if (inactive) {
8093 8094
        numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn);
        if (numInactivePools < 0) {
8095
            vshError(ctl, "%s", _("Failed to list inactive pools"));
E
Eric Blake 已提交
8096
            return false;
8097
        }
8098
    }
8099

8100 8101
    /* Determine the total number of pools to list */
    numAllPools = numActivePools + numInactivePools;
8102

8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114
    /* 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 已提交
8115
            return false;
8116 8117 8118
        }
    }

8119 8120 8121 8122 8123 8124 8125 8126
    /* 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 已提交
8127
            return false;
8128 8129
        }
    }
8130

8131 8132 8133 8134 8135 8136 8137 8138 8139 8140
    /* 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]);
8141
        if (!pool) {
8142
            VIR_FREE(poolNames[i]);
8143 8144 8145
            continue;
        }

8146
        /* Retrieve the autostart status of the pool */
8147
        if (virStoragePoolGetAutostart(pool, &autostart) < 0)
8148
            poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart"));
8149
        else
8150 8151 8152 8153 8154 8155
            poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ?
                                                    _("yes") : _("no"));

        /* Retrieve the persistence status of the pool */
        if (details) {
            persistent = virStoragePoolIsPersistent(pool);
8156 8157
            vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n",
                     persistent);
8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168
            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;
        }
8169

8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281
        /* 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 */
8282 8283 8284
        virStoragePoolFree(pool);
    }

8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302
    /* 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);
8303 8304
        }

8305
        /* Cleanup and return */
E
Eric Blake 已提交
8306
        functionReturn = true;
8307 8308
        goto cleanup;
    }
8309

8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347
    /* 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. */
8348
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest name string = %lu chars\n",
8349
             (unsigned long) nameStrLength);
8350
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest state string = %lu chars\n",
8351
             (unsigned long) stateStrLength);
8352
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest autostart string = %lu chars\n",
8353
             (unsigned long) autostartStrLength);
8354
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest persistent string = %lu chars\n",
8355
             (unsigned long) persistStrLength);
8356
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest capacity string = %lu chars\n",
8357
             (unsigned long) capStrLength);
8358
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest allocation string = %lu chars\n",
8359
             (unsigned long) allocStrLength);
8360
    vshDebug(ctl, VSH_ERR_DEBUG, "Longest available string = %lu chars\n",
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
             (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 已提交
8404
    functionReturn = true;
8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418
    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 已提交
8419
    functionReturn = false;
8420

8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432
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]);
8433
    }
8434 8435 8436 8437 8438 8439 8440

    /* Cleanup the memory for the initial arrays*/
    VIR_FREE(poolInfoTexts);
    VIR_FREE(poolNames);

    /* Return the desired value */
    return functionReturn;
8441 8442
}

8443 8444 8445 8446
/*
 * "find-storage-pool-sources-as" command
 */
static const vshCmdInfo info_find_storage_pool_sources_as[] = {
8447 8448
    {"help", N_("find potential storage pool sources")},
    {"desc", N_("Returns XML <sources> document.")},
8449 8450 8451 8452 8453
    {NULL, NULL}
};

static const vshCmdOptDef opts_find_storage_pool_sources_as[] = {
    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
8454 8455 8456
     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")},
8457
    {"initiator", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional initiator IQN to use for query")},
8458 8459 8460
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8461
static bool
8462 8463
cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
8464
    const char *type = NULL, *host = NULL;
8465 8466
    char *srcSpec = NULL;
    char *srcList;
8467
    const char *initiator = NULL;
8468

8469 8470
    if (vshCommandOptString(cmd, "type", &type) <= 0 ||
        vshCommandOptString(cmd, "host", &host) < 0 ||
8471 8472
        vshCommandOptString(cmd, "initiator", &initiator) < 0) {
        vshError(ctl,"%s", _("missing argument"));
E
Eric Blake 已提交
8473
        return false;
8474
    }
8475

8476
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8477
        return false;
8478 8479

    if (host) {
8480
        const char *port = NULL;
8481
        virBuffer buf = VIR_BUFFER_INITIALIZER;
8482 8483 8484 8485

        if (vshCommandOptString(cmd, "port", &port) < 0) {
            vshError(ctl, "%s", _("missing argument"));
            virBufferFreeAndReset(&buf);
E
Eric Blake 已提交
8486
            return false;
8487
        }
8488
        virBufferAddLit(&buf, "<source>\n");
8489
        virBufferAsprintf(&buf, "  <host name='%s'", host);
8490
        if (port)
8491
            virBufferAsprintf(&buf, " port='%s'", port);
8492
        virBufferAddLit(&buf, "/>\n");
8493 8494
        if (initiator) {
            virBufferAddLit(&buf, "  <initiator>\n");
8495
            virBufferAsprintf(&buf, "    <iqn name='%s'/>\n", initiator);
8496 8497
            virBufferAddLit(&buf, "  </initiator>\n");
        }
8498 8499 8500
        virBufferAddLit(&buf, "</source>\n");
        if (virBufferError(&buf)) {
            vshError(ctl, "%s", _("Out of memory"));
E
Eric Blake 已提交
8501
            return false;
8502
        }
8503
        srcSpec = virBufferContentAndReset(&buf);
8504 8505 8506
    }

    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
8507
    VIR_FREE(srcSpec);
8508
    if (srcList == NULL) {
8509
        vshError(ctl, _("Failed to find any %s pool sources"), type);
E
Eric Blake 已提交
8510
        return false;
8511 8512
    }
    vshPrint(ctl, "%s", srcList);
8513
    VIR_FREE(srcList);
8514

E
Eric Blake 已提交
8515
    return true;
8516 8517 8518 8519 8520 8521 8522
}


/*
 * "find-storage-pool-sources" command
 */
static const vshCmdInfo info_find_storage_pool_sources[] = {
8523 8524
    {"help", N_("discover potential storage pool sources")},
    {"desc", N_("Returns XML <sources> document.")},
8525 8526 8527 8528 8529
    {NULL, NULL}
};

static const vshCmdOptDef opts_find_storage_pool_sources[] = {
    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
8530
     N_("type of storage pool sources to discover")},
8531
    {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE,
8532
     N_("optional file of source xml to query for pools")},
8533 8534 8535
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8536
static bool
8537 8538
cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
8539
    const char *type = NULL, *srcSpecFile = NULL;
8540
    char *srcSpec = NULL, *srcList;
8541

8542
    if (vshCommandOptString(cmd, "type", &type) <= 0)
E
Eric Blake 已提交
8543
        return false;
8544

8545 8546
    if (vshCommandOptString(cmd, "srcSpec", &srcSpecFile) < 0) {
        vshError(ctl, "%s", _("missing option"));
E
Eric Blake 已提交
8547
        return false;
8548
    }
8549

8550
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8551
        return false;
8552 8553

    if (srcSpecFile && virFileReadAll(srcSpecFile, VIRSH_MAX_XML_FILE, &srcSpec) < 0)
E
Eric Blake 已提交
8554
        return false;
8555 8556

    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
8557
    VIR_FREE(srcSpec);
8558
    if (srcList == NULL) {
8559
        vshError(ctl, _("Failed to find any %s pool sources"), type);
E
Eric Blake 已提交
8560
        return false;
8561 8562
    }
    vshPrint(ctl, "%s", srcList);
8563
    VIR_FREE(srcList);
8564

E
Eric Blake 已提交
8565
    return true;
8566 8567 8568
}


8569 8570 8571
/*
 * "pool-info" command
 */
8572
static const vshCmdInfo info_pool_info[] = {
8573 8574
    {"help", N_("storage pool information")},
    {"desc", N_("Returns basic information about the storage pool.")},
8575 8576 8577
    {NULL, NULL}
};

8578
static const vshCmdOptDef opts_pool_info[] = {
8579
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
8580 8581 8582
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8583
static bool
8584
cmdPoolInfo(vshControl *ctl, const vshCmd *cmd)
8585 8586 8587
{
    virStoragePoolInfo info;
    virStoragePoolPtr pool;
8588 8589
    int autostart = 0;
    int persistent = 0;
E
Eric Blake 已提交
8590
    bool ret = true;
8591 8592
    char uuid[VIR_UUID_STRING_BUFLEN];

8593
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8594
        return false;
8595 8596

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
8597
        return false;
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

    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;
8624 8625 8626 8627
        case VIR_STORAGE_POOL_INACCESSIBLE:
            vshPrint(ctl, "%-15s %s\n", _("State:"),
                     _("inaccessible"));
            break;
8628 8629
        }

8630 8631
        /* Check and display whether the pool is persistent or not */
        persistent = virStoragePoolIsPersistent(pool);
8632 8633
        vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n",
                 persistent);
8634 8635 8636 8637 8638 8639 8640
        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);
8641 8642
        vshDebug(ctl, VSH_ERR_DEBUG, "Pool autostart flag value: %d\n",
                 autostart);
8643 8644 8645 8646 8647
        if (autostart < 0)
            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
        else
            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));

8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659
        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 已提交
8660
        ret = false;
8661 8662 8663 8664 8665 8666 8667 8668 8669 8670
    }

    virStoragePoolFree(pool);
    return ret;
}


/*
 * "pool-name" command
 */
8671
static const vshCmdInfo info_pool_name[] = {
8672
    {"help", N_("convert a pool UUID to pool name")},
8673
    {"desc", ""},
8674 8675 8676
    {NULL, NULL}
};

8677
static const vshCmdOptDef opts_pool_name[] = {
8678
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool uuid")},
8679 8680 8681
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8682
static bool
8683
cmdPoolName(vshControl *ctl, const vshCmd *cmd)
8684 8685 8686
{
    virStoragePoolPtr pool;

8687
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8688
        return false;
8689
    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
8690
                                           VSH_BYUUID)))
E
Eric Blake 已提交
8691
        return false;
8692 8693 8694

    vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
    virStoragePoolFree(pool);
E
Eric Blake 已提交
8695
    return true;
8696 8697 8698 8699 8700 8701
}


/*
 * "pool-start" command
 */
8702
static const vshCmdInfo info_pool_start[] = {
8703 8704
    {"help", N_("start a (previously defined) inactive pool")},
    {"desc", N_("Start a pool.")},
8705 8706 8707
    {NULL, NULL}
};

8708
static const vshCmdOptDef opts_pool_start[] = {
8709
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive pool")},
8710 8711 8712
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8713
static bool
8714
cmdPoolStart(vshControl *ctl, const vshCmd *cmd)
8715 8716
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
8717
    bool ret = true;
8718

8719
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8720
        return false;
8721

8722
    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME)))
E
Eric Blake 已提交
8723
         return false;
8724 8725 8726 8727 8728

    if (virStoragePoolCreate(pool, 0) == 0) {
        vshPrint(ctl, _("Pool %s started\n"),
                 virStoragePoolGetName(pool));
    } else {
8729
        vshError(ctl, _("Failed to start pool %s"), virStoragePoolGetName(pool));
E
Eric Blake 已提交
8730
        ret = false;
8731
    }
L
Laine Stump 已提交
8732 8733

    virStoragePoolFree(pool);
8734 8735 8736 8737
    return ret;
}


8738 8739 8740
/*
 * "vol-create-as" command
 */
8741
static const vshCmdInfo info_vol_create_as[] = {
8742 8743
    {"help", N_("create a volume from a set of args")},
    {"desc", N_("Create a vol.")},
8744 8745 8746
    {NULL, NULL}
};

8747
static const vshCmdOptDef opts_vol_create_as[] = {
8748 8749 8750 8751 8752
    {"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")},
8753 8754
    {"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")},
8755 8756 8757 8758 8759 8760 8761 8762 8763 8764
    {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 已提交
8765
        /* Deliberate fallthrough cases here :-) */
8766 8767 8768
        switch (*end) {
        case 'T':
            *val *= 1024;
8769
            /* fallthrough */
8770 8771
        case 'G':
            *val *= 1024;
8772
            /* fallthrough */
8773 8774
        case 'M':
            *val *= 1024;
8775
            /* fallthrough */
8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788
        case 'k':
            *val *= 1024;
            break;
        default:
            return -1;
        }
        end++;
        if (*end)
            return -1;
    }
    return 0;
}

E
Eric Blake 已提交
8789
static bool
8790
cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
8791 8792 8793
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
8794
    char *xml;
8795 8796
    const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL;
    const char *snapshotStrVol = NULL, *snapshotStrFormat = NULL;
8797
    unsigned long long capacity, allocation = 0;
8798
    virBuffer buf = VIR_BUFFER_INITIALIZER;
8799

8800
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8801
        return false;
8802 8803 8804

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
                                     VSH_BYNAME)))
E
Eric Blake 已提交
8805
        return false;
8806

8807
    if (vshCommandOptString(cmd, "name", &name) <= 0)
8808 8809
        goto cleanup;

8810
    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
8811 8812
        goto cleanup;
    if (cmdVolSize(capacityStr, &capacity) < 0)
8813
        vshError(ctl, _("Malformed size %s"), capacityStr);
8814

8815 8816
    if ((vshCommandOptString(cmd, "allocation", &allocationStr) > 0) &&
        (cmdVolSize(allocationStr, &allocation) < 0))
8817
        vshError(ctl, _("Malformed size %s"), allocationStr);
8818

8819 8820 8821 8822 8823 8824 8825
    if (vshCommandOptString(cmd, "format", &format) < 0 ||
        vshCommandOptString(cmd, "backing-vol", &snapshotStrVol) < 0 ||
        vshCommandOptString(cmd, "backing-vol-format",
                            &snapshotStrFormat) < 0) {
        vshError(ctl, "%s", _("missing argument"));
    }

8826

8827
    virBufferAddLit(&buf, "<volume>\n");
8828 8829
    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
    virBufferAsprintf(&buf, "  <capacity>%llu</capacity>\n", capacity);
8830
    if (allocationStr)
8831
        virBufferAsprintf(&buf, "  <allocation>%llu</allocation>\n", allocation);
8832 8833

    if (format) {
8834
        virBufferAddLit(&buf, "  <target>\n");
8835
        virBufferAsprintf(&buf, "    <format type='%s'/>\n",format);
8836
        virBufferAddLit(&buf, "  </target>\n");
8837
    }
8838

8839 8840 8841 8842
    /* Convert the snapshot parameters into backingStore XML */
    if (snapshotStrVol) {
        /* Lookup snapshot backing volume.  Try the backing-vol
         *  parameter as a name */
8843 8844
        vshDebug(ctl, VSH_ERR_DEBUG,
                 "%s: Look up backing store volume '%s' as name\n",
8845 8846 8847
                 cmd->def->name, snapshotStrVol);
        virStorageVolPtr snapVol = virStorageVolLookupByName(pool, snapshotStrVol);
        if (snapVol)
8848 8849
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as name\n",
8850 8851 8852 8853 8854
                         cmd->def->name, snapshotStrVol);

        if (snapVol == NULL) {
            /* Snapshot backing volume not found by name.  Try the
             *  backing-vol parameter as a key */
8855 8856
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: Look up backing store volume '%s' as key\n",
8857 8858 8859
                     cmd->def->name, snapshotStrVol);
            snapVol = virStorageVolLookupByKey(ctl->conn, snapshotStrVol);
            if (snapVol)
8860 8861
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as key\n",
8862 8863 8864 8865 8866
                         cmd->def->name, snapshotStrVol);
        }
        if (snapVol == NULL) {
            /* Snapshot backing volume not found by key.  Try the
             *  backing-vol parameter as a path */
8867 8868
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: Look up backing store volume '%s' as path\n",
8869 8870 8871
                     cmd->def->name, snapshotStrVol);
            snapVol = virStorageVolLookupByPath(ctl->conn, snapshotStrVol);
            if (snapVol)
8872 8873
                vshDebug(ctl, VSH_ERR_DEBUG,
                         "%s: Backing store volume found using '%s' as path\n",
8874 8875 8876 8877
                         cmd->def->name, snapshotStrVol);
        }
        if (snapVol == NULL) {
            vshError(ctl, _("failed to get vol '%s'"), snapshotStrVol);
8878
            goto cleanup;
8879 8880 8881 8882 8883
        }

        char *snapshotStrVolPath;
        if ((snapshotStrVolPath = virStorageVolGetPath(snapVol)) == NULL) {
            virStorageVolFree(snapVol);
8884
            goto cleanup;
8885 8886 8887 8888
        }

        /* Create XML for the backing store */
        virBufferAddLit(&buf, "  <backingStore>\n");
8889
        virBufferAsprintf(&buf, "    <path>%s</path>\n",snapshotStrVolPath);
8890
        if (snapshotStrFormat)
8891
            virBufferAsprintf(&buf, "    <format type='%s'/>\n",snapshotStrFormat);
8892 8893 8894 8895 8896 8897 8898 8899
        virBufferAddLit(&buf, "  </backingStore>\n");

        /* Cleanup snapshot allocations */
        VIR_FREE(snapshotStrVolPath);
        virStorageVolFree(snapVol);
    }

    virBufferAddLit(&buf, "</volume>\n");
8900

8901 8902
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
8903
        goto cleanup;
8904 8905 8906
    }
    xml = virBufferContentAndReset(&buf);
    vol = virStorageVolCreateXML(pool, xml, 0);
8907
    VIR_FREE(xml);
8908 8909 8910 8911 8912
    virStoragePoolFree(pool);

    if (vol != NULL) {
        vshPrint(ctl, _("Vol %s created\n"), name);
        virStorageVolFree(vol);
E
Eric Blake 已提交
8913
        return true;
8914
    } else {
8915
        vshError(ctl, _("Failed to create vol %s"), name);
E
Eric Blake 已提交
8916
        return false;
8917 8918 8919
    }

 cleanup:
8920
    virBufferFreeAndReset(&buf);
8921
    virStoragePoolFree(pool);
E
Eric Blake 已提交
8922
    return false;
8923 8924 8925
}


8926 8927 8928
/*
 * "pool-undefine" command
 */
8929
static const vshCmdInfo info_pool_undefine[] = {
8930 8931
    {"help", N_("undefine an inactive pool")},
    {"desc", N_("Undefine the configuration for an inactive pool.")},
8932 8933 8934
    {NULL, NULL}
};

8935
static const vshCmdOptDef opts_pool_undefine[] = {
8936
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
8937 8938 8939
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8940
static bool
8941
cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd)
8942 8943
{
    virStoragePoolPtr pool;
E
Eric Blake 已提交
8944
    bool ret = true;
8945
    const char *name;
8946

8947
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8948
        return false;
8949 8950

    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
E
Eric Blake 已提交
8951
        return false;
8952 8953 8954 8955

    if (virStoragePoolUndefine(pool) == 0) {
        vshPrint(ctl, _("Pool %s has been undefined\n"), name);
    } else {
8956
        vshError(ctl, _("Failed to undefine pool %s"), name);
E
Eric Blake 已提交
8957
        ret = false;
8958 8959
    }

L
Laine Stump 已提交
8960
    virStoragePoolFree(pool);
8961 8962 8963 8964 8965 8966 8967
    return ret;
}


/*
 * "pool-uuid" command
 */
8968
static const vshCmdInfo info_pool_uuid[] = {
8969
    {"help", N_("convert a pool name to pool UUID")},
8970
    {"desc", ""},
8971 8972 8973
    {NULL, NULL}
};

8974
static const vshCmdOptDef opts_pool_uuid[] = {
8975
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
8976 8977 8978
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
8979
static bool
8980
cmdPoolUuid(vshControl *ctl, const vshCmd *cmd)
8981 8982 8983 8984
{
    virStoragePoolPtr pool;
    char uuid[VIR_UUID_STRING_BUFLEN];

8985
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
8986
        return false;
8987 8988

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
8989
                                           VSH_BYNAME)))
E
Eric Blake 已提交
8990
        return false;
8991 8992 8993 8994

    if (virStoragePoolGetUUIDString(pool, uuid) != -1)
        vshPrint(ctl, "%s\n", uuid);
    else
8995
        vshError(ctl, "%s", _("failed to get pool UUID"));
8996

L
Laine Stump 已提交
8997
    virStoragePoolFree(pool);
E
Eric Blake 已提交
8998
    return true;
8999 9000 9001 9002 9003 9004
}


/*
 * "vol-create" command
 */
9005
static const vshCmdInfo info_vol_create[] = {
9006 9007
    {"help", N_("create a vol from an XML file")},
    {"desc", N_("Create a vol.")},
9008 9009 9010
    {NULL, NULL}
};

9011
static const vshCmdOptDef opts_vol_create[] = {
9012 9013
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
9014 9015 9016
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9017
static bool
9018
cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
9019 9020 9021
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
9022
    const char *from = NULL;
E
Eric Blake 已提交
9023
    bool ret = true;
9024 9025
    char *buffer;

9026
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9027
        return false;
9028 9029

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
9030
                                           VSH_BYNAME)))
E
Eric Blake 已提交
9031
        return false;
9032

9033
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
9034
        virStoragePoolFree(pool);
E
Eric Blake 已提交
9035
        return false;
9036 9037 9038
    }

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
9039
        virshReportError(ctl);
9040
        virStoragePoolFree(pool);
E
Eric Blake 已提交
9041
        return false;
9042 9043 9044
    }

    vol = virStorageVolCreateXML(pool, buffer, 0);
9045
    VIR_FREE(buffer);
9046 9047 9048 9049 9050 9051 9052
    virStoragePoolFree(pool);

    if (vol != NULL) {
        vshPrint(ctl, _("Vol %s created from %s\n"),
                 virStorageVolGetName(vol), from);
        virStorageVolFree(vol);
    } else {
9053
        vshError(ctl, _("Failed to create vol from %s"), from);
E
Eric Blake 已提交
9054
        ret = false;
9055 9056 9057 9058
    }
    return ret;
}

9059 9060 9061 9062
/*
 * "vol-create-from" command
 */
static const vshCmdInfo info_vol_create_from[] = {
9063 9064
    {"help", N_("create a vol, using another volume as input")},
    {"desc", N_("Create a vol from an existing volume.")},
9065 9066 9067 9068
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_create_from[] = {
9069 9070 9071
    {"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 已提交
9072
    {"inputpool", VSH_OT_STRING, 0, N_("pool name or uuid of the input volume's pool")},
9073 9074 9075
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9076
static bool
9077 9078 9079 9080
cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool = NULL;
    virStorageVolPtr newvol = NULL, inputvol = NULL;
9081
    const char *from = NULL;
E
Eric Blake 已提交
9082
    bool ret = false;
9083 9084
    char *buffer = NULL;

9085
    if (!vshConnectionUsability(ctl, ctl->conn))
9086 9087 9088 9089 9090
        goto cleanup;

    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME)))
        goto cleanup;

9091
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
9092 9093 9094 9095 9096 9097 9098
        goto cleanup;
    }

    if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL)))
        goto cleanup;

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
9099
        virshReportError(ctl);
9100 9101 9102 9103 9104 9105 9106 9107 9108
        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 {
9109
        vshError(ctl, _("Failed to create vol from %s"), from);
9110 9111 9112
        goto cleanup;
    }

E
Eric Blake 已提交
9113
    ret = true;
9114
cleanup:
9115
    VIR_FREE(buffer);
9116 9117 9118 9119
    if (pool)
        virStoragePoolFree(pool);
    if (inputvol)
        virStorageVolFree(inputvol);
L
Laine Stump 已提交
9120 9121
    if (newvol)
        virStorageVolFree(newvol);
9122 9123 9124 9125
    return ret;
}

static xmlChar *
9126 9127
makeCloneXML(const char *origxml, const char *newname)
{
9128

9129 9130 9131
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
9132 9133 9134
    xmlChar *newxml = NULL;
    int size;

9135
    doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157
    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[] = {
9158 9159
    {"help", N_("clone a volume.")},
    {"desc", N_("Clone an existing volume.")},
9160 9161 9162 9163
    {NULL, NULL}
};

static const vshCmdOptDef opts_vol_clone[] = {
9164 9165
    {"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 已提交
9166
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
9167 9168 9169
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9170
static bool
9171 9172 9173 9174
cmdVolClone(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr origpool = NULL;
    virStorageVolPtr origvol = NULL, newvol = NULL;
9175
    const char *name = NULL;
9176
    char *origxml = NULL;
9177
    xmlChar *newxml = NULL;
E
Eric Blake 已提交
9178
    bool ret = false;
9179

9180
    if (!vshConnectionUsability(ctl, ctl->conn))
9181 9182 9183 9184 9185 9186 9187
        goto cleanup;

    if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
        goto cleanup;

    origpool = virStoragePoolLookupByVolume(origvol);
    if (!origpool) {
9188
        vshError(ctl, "%s", _("failed to get parent pool"));
9189 9190 9191
        goto cleanup;
    }

9192
    if (vshCommandOptString(cmd, "newname", &name) <= 0)
9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210
        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 {
9211
        vshError(ctl, _("Failed to clone vol from %s"),
9212 9213 9214 9215
                 virStorageVolGetName(origvol));
        goto cleanup;
    }

E
Eric Blake 已提交
9216
    ret = true;
9217 9218

cleanup:
9219
    VIR_FREE(origxml);
9220 9221 9222
    xmlFree(newxml);
    if (origvol)
        virStorageVolFree(origvol);
L
Laine Stump 已提交
9223 9224
    if (newvol)
        virStorageVolFree(newvol);
9225 9226 9227 9228 9229
    if (origpool)
        virStoragePoolFree(origpool);
    return ret;
}

9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242

/*
 * "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 已提交
9243
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257
    {"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 已提交
9258
static bool
9259 9260 9261 9262
cmdVolUpload (vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    virStorageVolPtr vol = NULL;
E
Eric Blake 已提交
9263
    bool ret = false;
9264 9265 9266 9267 9268 9269 9270 9271 9272 9273
    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 已提交
9274
        return false;
9275 9276 9277 9278
    }

    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
9279
        return false;
9280 9281 9282
    }

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
9283
        return false;
9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317
    }

    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 已提交
9318
    ret = true;
9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 9341 9342

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 已提交
9343
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
9344 9345 9346 9347 9348
    {"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 已提交
9349
static bool
9350 9351 9352 9353
cmdVolDownload (vshControl *ctl, const vshCmd *cmd)
{
    const char *file = NULL;
    virStorageVolPtr vol = NULL;
E
Eric Blake 已提交
9354
    bool ret = false;
9355 9356 9357 9358
    int fd = -1;
    virStreamPtr st = NULL;
    const char *name = NULL;
    unsigned long long offset = 0, length = 0;
9359
    bool created = false;
9360 9361

    if (!vshConnectionUsability(ctl, ctl->conn))
9362
        return false;
9363 9364 9365

    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
9366
        return false;
9367 9368 9369 9370
    }

    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
        vshError(ctl, _("Unable to parse integer"));
E
Eric Blake 已提交
9371
        return false;
9372 9373 9374
    }

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name)))
E
Eric Blake 已提交
9375
        return false;
9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387

    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;
        }
9388 9389
    } else {
        created = true;
9390 9391 9392 9393 9394 9395 9396 9397
    }

    st = virStreamNew(ctl->conn, 0);
    if (virStorageVolDownload(vol, st, offset, length, 0) < 0) {
        vshError(ctl, _("cannot download from volume %s"), name);
        goto cleanup;
    }

9398
    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413
        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 已提交
9414
    ret = true;
9415 9416

cleanup:
9417
    VIR_FORCE_CLOSE(fd);
9418
    if (!ret && created)
9419 9420 9421 9422 9423 9424 9425 9426 9427
        unlink(file);
    if (vol)
        virStorageVolFree(vol);
    if (st)
        virStreamFree(st);
    return ret;
}


9428 9429 9430
/*
 * "vol-delete" command
 */
9431
static const vshCmdInfo info_vol_delete[] = {
9432 9433
    {"help", N_("delete a vol")},
    {"desc", N_("Delete a given vol.")},
9434 9435 9436
    {NULL, NULL}
};

9437
static const vshCmdOptDef opts_vol_delete[] = {
9438
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
9439
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
9440 9441 9442
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9443
static bool
9444
cmdVolDelete(vshControl *ctl, const vshCmd *cmd)
9445 9446
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
9447
    bool ret = true;
9448
    const char *name;
9449

9450
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9451
        return false;
9452 9453

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
9454
        return false;
9455 9456 9457
    }

    if (virStorageVolDelete(vol, 0) == 0) {
D
Daniel Veillard 已提交
9458
        vshPrint(ctl, _("Vol %s deleted\n"), name);
9459
    } else {
9460
        vshError(ctl, _("Failed to delete vol %s"), name);
E
Eric Blake 已提交
9461
        ret = false;
9462 9463
    }

L
Laine Stump 已提交
9464
    virStorageVolFree(vol);
9465 9466 9467 9468
    return ret;
}


D
David Allan 已提交
9469 9470 9471 9472 9473 9474 9475 9476 9477 9478 9479
/*
 * "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 已提交
9480
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
D
David Allan 已提交
9481 9482 9483
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9484
static bool
D
David Allan 已提交
9485 9486 9487
cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
9488
    bool ret = true;
9489
    const char *name;
D
David Allan 已提交
9490

9491
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9492
        return false;
D
David Allan 已提交
9493 9494

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
E
Eric Blake 已提交
9495
        return false;
D
David Allan 已提交
9496 9497 9498 9499 9500 9501
    }

    if (virStorageVolWipe(vol, 0) == 0) {
        vshPrint(ctl, _("Vol %s wiped\n"), name);
    } else {
        vshError(ctl, _("Failed to wipe vol %s"), name);
E
Eric Blake 已提交
9502
        ret = false;
D
David Allan 已提交
9503 9504 9505 9506 9507 9508 9509
    }

    virStorageVolFree(vol);
    return ret;
}


9510 9511 9512
/*
 * "vol-info" command
 */
9513
static const vshCmdInfo info_vol_info[] = {
9514 9515
    {"help", N_("storage vol information")},
    {"desc", N_("Returns basic information about the storage vol.")},
9516 9517 9518
    {NULL, NULL}
};

9519
static const vshCmdOptDef opts_vol_info[] = {
9520
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
9521
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
9522 9523 9524
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9525
static bool
9526
cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
9527 9528 9529
{
    virStorageVolInfo info;
    virStorageVolPtr vol;
E
Eric Blake 已提交
9530
    bool ret = true;
9531

9532
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9533
        return false;
9534 9535

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
9536
        return false;
9537 9538 9539 9540 9541 9542

    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol));

    if (virStorageVolGetInfo(vol, &info) == 0) {
        double val;
        const char *unit;
R
Ryota Ozaki 已提交
9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558
        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"));
        }
9559 9560 9561 9562 9563 9564 9565

        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 已提交
9566
        ret = false;
9567 9568 9569 9570 9571 9572 9573 9574 9575 9576
    }

    virStorageVolFree(vol);
    return ret;
}


/*
 * "vol-dumpxml" command
 */
9577
static const vshCmdInfo info_vol_dumpxml[] = {
9578 9579
    {"help", N_("vol information in XML")},
    {"desc", N_("Output the vol information as an XML dump to stdout.")},
9580 9581 9582
    {NULL, NULL}
};

9583
static const vshCmdOptDef opts_vol_dumpxml[] = {
9584
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
E
Eric Blake 已提交
9585
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
9586 9587 9588
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9589
static bool
9590
cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
9591 9592
{
    virStorageVolPtr vol;
E
Eric Blake 已提交
9593
    bool ret = true;
9594 9595
    char *dump;

9596
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9597
        return false;
9598 9599

    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
9600
        return false;
9601 9602 9603

    dump = virStorageVolGetXMLDesc(vol, 0);
    if (dump != NULL) {
9604
        vshPrint(ctl, "%s", dump);
9605
        VIR_FREE(dump);
9606
    } else {
E
Eric Blake 已提交
9607
        ret = false;
9608 9609 9610 9611 9612 9613 9614 9615 9616 9617
    }

    virStorageVolFree(vol);
    return ret;
}


/*
 * "vol-list" command
 */
9618
static const vshCmdInfo info_vol_list[] = {
9619 9620
    {"help", N_("list vols")},
    {"desc", N_("Returns list of vols by pool.")},
9621 9622 9623
    {NULL, NULL}
};

9624
static const vshCmdOptDef opts_vol_list[] = {
9625
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
9626
    {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")},
9627 9628 9629
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9630
static bool
9631
cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
9632
{
9633
    virStorageVolInfo volumeInfo;
9634 9635
    virStoragePoolPtr pool;
    char **activeNames = NULL;
9636 9637 9638 9639 9640
    char *outputStr = NULL;
    const char *unit;
    double val;
    int details = vshCommandOptBool(cmd, "details");
    int numVolumes = 0, i;
W
Wen Congyang 已提交
9641 9642
    int ret;
    bool functionReturn;
9643 9644 9645 9646 9647 9648 9649 9650 9651 9652 9653
    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;
9654

9655
    /* Check the connection to libvirtd daemon is still working */
9656
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9657
        return false;
9658

9659
    /* Look up the pool information given to us by the user */
9660
    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
E
Eric Blake 已提交
9661
        return false;
9662

9663 9664
    /* Determine the number of volumes in the pool */
    numVolumes = virStoragePoolNumOfVolumes(pool);
9665

9666 9667 9668
    if (numVolumes < 0) {
        vshError(ctl, "%s", _("Failed to list storage volumes"));
        virStoragePoolFree(pool);
E
Eric Blake 已提交
9669
        return false;
9670 9671
    }

9672 9673 9674 9675 9676
    /* 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) {
9677
            vshError(ctl, "%s", _("Failed to list active vols"));
9678
            VIR_FREE(activeNames);
9679
            virStoragePoolFree(pool);
E
Eric Blake 已提交
9680
            return false;
9681 9682
        }

9683 9684 9685 9686 9687
        /* Sort the volume names */
        qsort(&activeNames[0], numVolumes, sizeof(*activeNames), namesorter);

        /* Set aside memory for volume information pointers */
        volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts));
9688 9689
    }

9690 9691 9692 9693 9694
    /* Collect the rest of the volume information for display */
    for (i = 0; i < numVolumes; i++) {
        /* Retrieve volume info */
        virStorageVolPtr vol = virStorageVolLookupByName(pool,
                                                         activeNames[i]);
9695

9696 9697 9698 9699
        /* 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"));
9700 9701
        }

9702 9703 9704 9705 9706 9707 9708 9709 9710
        /* 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 */
9711

9712
                /* Volume type */
9713 9714 9715 9716 9717 9718 9719 9720 9721 9722 9723 9724 9725
                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"));
                }
9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 9770 9771 9772 9773 9774 9775

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

9777
        /* Cleanup memory allocation */
9778
        virStorageVolFree(vol);
9779 9780 9781 9782 9783 9784 9785 9786 9787 9788 9789 9790 9791 9792 9793 9794 9795 9796
    }

    /* 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 已提交
9797
        functionReturn = true;
9798 9799 9800 9801 9802 9803 9804 9805 9806 9807 9808 9809 9810 9811 9812 9813 9814 9815 9816 9817 9818 9819 9820 9821 9822 9823 9824 9825 9826 9827 9828
        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 */
9829 9830 9831 9832 9833 9834 9835 9836 9837 9838
    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);
9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872

    /* 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 已提交
9873
    functionReturn = true;
9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887
    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 已提交
9888
    functionReturn = false;
9889 9890 9891 9892 9893 9894 9895 9896 9897 9898

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);
9899
        VIR_FREE(activeNames[i]);
9900
    }
9901 9902 9903 9904

    /* Cleanup remaining memory */
    VIR_FREE(outputStr);
    VIR_FREE(volInfoTexts);
9905
    VIR_FREE(activeNames);
9906
    virStoragePoolFree(pool);
9907 9908 9909

    /* Return the desired value */
    return functionReturn;
9910 9911 9912 9913 9914 9915
}


/*
 * "vol-name" command
 */
9916
static const vshCmdInfo info_vol_name[] = {
9917
    {"help", N_("returns the volume name for a given volume key or path")},
9918
    {"desc", ""},
9919 9920 9921
    {NULL, NULL}
};

9922
static const vshCmdOptDef opts_vol_name[] = {
9923
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
9924 9925 9926
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9927
static bool
9928
cmdVolName(vshControl *ctl, const vshCmd *cmd)
9929 9930 9931
{
    virStorageVolPtr vol;

9932
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9933
        return false;
9934

9935
    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
9936
                                   VSH_BYUUID)))
E
Eric Blake 已提交
9937
        return false;
9938 9939 9940

    vshPrint(ctl, "%s\n", virStorageVolGetName(vol));
    virStorageVolFree(vol);
E
Eric Blake 已提交
9941
    return true;
9942 9943 9944
}


J
Justin Clift 已提交
9945 9946 9947 9948 9949 9950 9951 9952 9953 9954
/*
 * "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[] = {
9955
    {"uuid", VSH_OT_BOOL, 0, N_("return the pool uuid rather than pool name")},
J
Justin Clift 已提交
9956 9957 9958 9959
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
9960
static bool
J
Justin Clift 已提交
9961 9962 9963 9964
cmdVolPool(vshControl *ctl, const vshCmd *cmd)
{
    virStoragePoolPtr pool;
    virStorageVolPtr vol;
9965
    char uuid[VIR_UUID_STRING_BUFLEN];
J
Justin Clift 已提交
9966 9967

    /* Check the connection to libvirtd daemon is still working */
9968
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
9969
        return false;
J
Justin Clift 已提交
9970 9971

    /* Use the supplied string to locate the volume */
9972
    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
J
Justin Clift 已提交
9973
                                   VSH_BYUUID))) {
E
Eric Blake 已提交
9974
        return false;
J
Justin Clift 已提交
9975 9976 9977 9978 9979 9980 9981
    }

    /* 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 已提交
9982
        return false;
J
Justin Clift 已提交
9983 9984
    }

9985 9986 9987 9988 9989 9990 9991 9992 9993
    /* 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 已提交
9994 9995 9996 9997

    /* Cleanup */
    virStorageVolFree(vol);
    virStoragePoolFree(pool);
E
Eric Blake 已提交
9998
    return true;
J
Justin Clift 已提交
9999 10000
}

10001 10002 10003 10004

/*
 * "vol-key" command
 */
10005
static const vshCmdInfo info_vol_key[] = {
10006
    {"help", N_("returns the volume key for a given volume name or path")},
10007
    {"desc", ""},
10008 10009 10010
    {NULL, NULL}
};

10011
static const vshCmdOptDef opts_vol_key[] = {
10012
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or path")},
E
Eric Blake 已提交
10013
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
10014 10015 10016
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10017
static bool
10018
cmdVolKey(vshControl *ctl, const vshCmd *cmd)
10019 10020 10021
{
    virStorageVolPtr vol;

10022
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10023
        return false;
10024

10025
    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
E
Eric Blake 已提交
10026
        return false;
10027 10028 10029

    vshPrint(ctl, "%s\n", virStorageVolGetKey(vol));
    virStorageVolFree(vol);
E
Eric Blake 已提交
10030
    return true;
10031 10032 10033 10034 10035 10036 10037
}



/*
 * "vol-path" command
 */
10038
static const vshCmdInfo info_vol_path[] = {
10039
    {"help", N_("returns the volume path for a given volume name or key")},
10040
    {"desc", ""},
10041 10042 10043
    {NULL, NULL}
};

10044
static const vshCmdOptDef opts_vol_path[] = {
10045
    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or key")},
E
Eric Blake 已提交
10046
    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
10047 10048 10049
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10050
static bool
10051
cmdVolPath(vshControl *ctl, const vshCmd *cmd)
10052 10053
{
    virStorageVolPtr vol;
10054
    char * StorageVolPath;
10055

10056
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10057
        return false;
10058

10059
    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) {
E
Eric Blake 已提交
10060
        return false;
10061
    }
10062

10063 10064 10065 10066 10067 10068 10069
    if ((StorageVolPath = virStorageVolGetPath(vol)) == NULL) {
        virStorageVolFree(vol);
        return false;
    }

    vshPrint(ctl, "%s\n", StorageVolPath);
    VIR_FREE(StorageVolPath);
10070
    virStorageVolFree(vol);
E
Eric Blake 已提交
10071
    return true;
10072 10073 10074
}


10075 10076 10077 10078
/*
 * "secret-define" command
 */
static const vshCmdInfo info_secret_define[] = {
10079 10080
    {"help", N_("define or modify a secret from an XML file")},
    {"desc", N_("Define or modify a secret.")},
10081 10082
    {NULL, NULL}
};
10083

10084
static const vshCmdOptDef opts_secret_define[] = {
10085
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
10086 10087
    {NULL, 0, 0, NULL}
};
10088

E
Eric Blake 已提交
10089
static bool
10090 10091
cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
{
10092
    const char *from = NULL;
10093
    char *buffer;
10094
    virSecretPtr res;
10095
    char uuid[VIR_UUID_STRING_BUFLEN];
10096

10097
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10098
        return false;
10099

10100
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
10101
        return false;
10102 10103

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

    res = virSecretDefineXML(ctl->conn, buffer, 0);
10107
    VIR_FREE(buffer);
10108 10109

    if (res == NULL) {
10110
        vshError(ctl, _("Failed to set attributes from %s"), from);
E
Eric Blake 已提交
10111
        return false;
10112
    }
10113
    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
10114
        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
10115
        virSecretFree(res);
E
Eric Blake 已提交
10116
        return false;
10117 10118 10119
    }
    vshPrint(ctl, _("Secret %s created\n"), uuid);
    virSecretFree(res);
E
Eric Blake 已提交
10120
    return true;
10121 10122 10123 10124 10125 10126
}

/*
 * "secret-dumpxml" command
 */
static const vshCmdInfo info_secret_dumpxml[] = {
10127 10128
    {"help", N_("secret attributes in XML")},
    {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
10129 10130 10131 10132
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_dumpxml[] = {
10133
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
10134 10135 10136
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10137
static bool
10138 10139 10140
cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
E
Eric Blake 已提交
10141
    bool ret = false;
10142 10143
    char *xml;

10144
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10145
        return false;
10146 10147 10148

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
10149
        return false;
10150 10151 10152 10153

    xml = virSecretGetXMLDesc(secret, 0);
    if (xml == NULL)
        goto cleanup;
10154
    vshPrint(ctl, "%s", xml);
10155
    VIR_FREE(xml);
E
Eric Blake 已提交
10156
    ret = true;
10157 10158 10159 10160 10161 10162 10163 10164 10165 10166

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-set-value" command
 */
static const vshCmdInfo info_secret_set_value[] = {
10167 10168
    {"help", N_("set a secret value")},
    {"desc", N_("Set a secret value.")},
10169 10170 10171 10172
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_set_value[] = {
10173 10174
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
    {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
10175 10176 10177
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10178
static bool
10179 10180 10181 10182
cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    size_t value_size;
10183
    const char *base64 = NULL;
10184
    char *value;
E
Eric Blake 已提交
10185 10186
    int res;
    bool ret = false;
10187

10188
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10189
        return false;
10190 10191 10192

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
10193
        return false;
10194

10195
    if (vshCommandOptString(cmd, "base64", &base64) <= 0)
10196 10197 10198
        goto cleanup;

    if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
J
Jim Meyering 已提交
10199
        vshError(ctl, "%s", _("Invalid base64 data"));
10200 10201 10202
        goto cleanup;
    }
    if (value == NULL) {
10203
        vshError(ctl, "%s", _("Failed to allocate memory"));
E
Eric Blake 已提交
10204
        return false;
10205 10206 10207 10208
    }

    res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
    memset(value, 0, value_size);
10209
    VIR_FREE(value);
10210 10211

    if (res != 0) {
10212
        vshError(ctl, "%s", _("Failed to set secret value"));
10213 10214 10215
        goto cleanup;
    }
    vshPrint(ctl, "%s", _("Secret value set\n"));
E
Eric Blake 已提交
10216
    ret = true;
10217 10218 10219 10220 10221 10222 10223 10224 10225 10226

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-get-value" command
 */
static const vshCmdInfo info_secret_get_value[] = {
10227 10228
    {"help", N_("Output a secret value")},
    {"desc", N_("Output a secret value to stdout.")},
10229 10230 10231 10232
    {NULL, NULL}
};

static const vshCmdOptDef opts_secret_get_value[] = {
10233
    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
10234 10235 10236
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10237
static bool
10238 10239 10240 10241 10242 10243
cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    char *base64;
    unsigned char *value;
    size_t value_size;
E
Eric Blake 已提交
10244
    bool ret = false;
10245

10246
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10247
        return false;
10248 10249 10250

    secret = vshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
E
Eric Blake 已提交
10251
        return false;
10252 10253 10254 10255 10256 10257 10258

    value = virSecretGetValue(secret, &value_size, 0);
    if (value == NULL)
        goto cleanup;

    base64_encode_alloc((char *)value, value_size, &base64);
    memset(value, 0, value_size);
10259
    VIR_FREE(value);
10260 10261

    if (base64 == NULL) {
10262
        vshError(ctl, "%s", _("Failed to allocate memory"));
10263 10264
        goto cleanup;
    }
10265
    vshPrint(ctl, "%s", base64);
10266
    memset(base64, 0, strlen(base64));
10267
    VIR_FREE(base64);
E
Eric Blake 已提交
10268
    ret = true;
10269 10270 10271 10272 10273 10274 10275 10276 10277 10278

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-undefine" command
 */
static const vshCmdInfo info_secret_undefine[] = {
10279 10280
    {"help", N_("undefine a secret")},
    {"desc", N_("Undefine a secret.")},
10281 10282 10283 10284
    {NULL, NULL}
};

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

E
Eric Blake 已提交
10289
static bool
10290 10291 10292
cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
E
Eric Blake 已提交
10293
    bool ret = false;
10294
    const char *uuid;
10295

10296
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10297
        return false;
10298 10299 10300

    secret = vshCommandOptSecret(ctl, cmd, &uuid);
    if (secret == NULL)
E
Eric Blake 已提交
10301
        return false;
10302 10303

    if (virSecretUndefine(secret) < 0) {
10304
        vshError(ctl, _("Failed to delete secret %s"), uuid);
10305 10306 10307
        goto cleanup;
    }
    vshPrint(ctl, _("Secret %s deleted\n"), uuid);
E
Eric Blake 已提交
10308
    ret = true;
10309 10310 10311 10312 10313 10314 10315 10316 10317 10318

cleanup:
    virSecretFree(secret);
    return ret;
}

/*
 * "secret-list" command
 */
static const vshCmdInfo info_secret_list[] = {
10319 10320
    {"help", N_("list secrets")},
    {"desc", N_("Returns a list of secrets")},
10321 10322 10323
    {NULL, NULL}
};

E
Eric Blake 已提交
10324
static bool
10325 10326 10327 10328 10329
cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    int maxuuids = 0, i;
    char **uuids = NULL;

10330
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10331
        return false;
10332 10333 10334

    maxuuids = virConnectNumOfSecrets(ctl->conn);
    if (maxuuids < 0) {
10335
        vshError(ctl, "%s", _("Failed to list secrets"));
E
Eric Blake 已提交
10336
        return false;
10337 10338 10339 10340 10341
    }
    uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);

    maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
    if (maxuuids < 0) {
10342
        vshError(ctl, "%s", _("Failed to list secrets"));
10343
        VIR_FREE(uuids);
E
Eric Blake 已提交
10344
        return false;
10345 10346 10347 10348
    }

    qsort(uuids, maxuuids, sizeof(char *), namesorter);

10349 10350
    vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
    vshPrintExtra(ctl, "-----------------------------------------------------------\n");
10351 10352

    for (i = 0; i < maxuuids; i++) {
10353 10354 10355 10356
        virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]);
        const char *usageType = NULL;

        if (!sec) {
10357
            VIR_FREE(uuids[i]);
10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375
            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);
10376
        VIR_FREE(uuids[i]);
10377
    }
10378
    VIR_FREE(uuids);
E
Eric Blake 已提交
10379
    return true;
10380
}
10381 10382 10383 10384 10385


/*
 * "version" command
 */
10386
static const vshCmdInfo info_version[] = {
10387 10388
    {"help", N_("show version")},
    {"desc", N_("Display the system version information.")},
10389 10390 10391
    {NULL, NULL}
};

10392 10393 10394 10395
static const vshCmdOptDef opts_version[] = {
    {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
    {NULL, 0, 0, NULL}
};
10396

E
Eric Blake 已提交
10397
static bool
10398
cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
10399 10400 10401 10402 10403 10404
{
    unsigned long hvVersion;
    const char *hvType;
    unsigned long libVersion;
    unsigned long includeVersion;
    unsigned long apiVersion;
10405
    unsigned long daemonVersion;
10406 10407 10408 10409 10410
    int ret;
    unsigned int major;
    unsigned int minor;
    unsigned int rel;

10411
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10412
        return false;
10413 10414 10415

    hvType = virConnectGetType(ctl->conn);
    if (hvType == NULL) {
10416
        vshError(ctl, "%s", _("failed to get hypervisor type"));
E
Eric Blake 已提交
10417
        return false;
10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429
    }

    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) {
10430
        vshError(ctl, "%s", _("failed to get the library version"));
E
Eric Blake 已提交
10431
        return false;
10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448
    }
    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) {
10449
        vshError(ctl, "%s", _("failed to get the hypervisor version"));
E
Eric Blake 已提交
10450
        return false;
10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463
    }
    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);
    }
10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478

    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 已提交
10479
    return true;
10480 10481
}

10482 10483 10484 10485
/*
 * "nodedev-list" command
 */
static const vshCmdInfo info_node_list_devices[] = {
10486
    {"help", N_("enumerate devices on this host")},
10487
    {"desc", ""},
10488 10489 10490 10491
    {NULL, NULL}
};

static const vshCmdOptDef opts_node_list_devices[] = {
10492 10493
    {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
    {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
10494 10495 10496
    {NULL, 0, 0, NULL}
};

10497 10498 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518
#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] = '-';
10519 10520
        indentBuf[indentIdx+2] = ' ';
        indentBuf[indentIdx+3] = '\0';
10521 10522 10523
    }

    /* Print this device */
10524
    vshPrint(ctl, "%s", indentBuf);
10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547
    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) {
10548
        vshPrint(ctl, "%s", indentBuf);
10549
        vshPrint(ctl, " |\n");
10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571
    }

    /* 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) {
10572
        vshPrint(ctl, "%s", indentBuf);
10573 10574 10575 10576
        vshPrint(ctl, "\n");
    }
}

E
Eric Blake 已提交
10577
static bool
10578 10579
cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
10580
    const char *cap = NULL;
10581
    char **devices;
10582
    int num_devices, i;
10583
    int tree = vshCommandOptBool(cmd, "tree");
10584

10585
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10586
        return false;
10587

10588
    if (vshCommandOptString(cmd, "cap", &cap) <= 0)
10589 10590 10591 10592
        cap = NULL;

    num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
    if (num_devices < 0) {
10593
        vshError(ctl, "%s", _("Failed to count node devices"));
E
Eric Blake 已提交
10594
        return false;
10595
    } else if (num_devices == 0) {
E
Eric Blake 已提交
10596
        return true;
10597 10598 10599 10600 10601 10602
    }

    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
    num_devices =
        virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
    if (num_devices < 0) {
10603
        vshError(ctl, "%s", _("Failed to list node devices"));
10604
        VIR_FREE(devices);
E
Eric Blake 已提交
10605
        return false;
10606
    }
10607
    qsort(&devices[0], num_devices, sizeof(char*), namesorter);
10608 10609 10610 10611 10612 10613 10614
    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 已提交
10615
                parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
10616 10617 10618 10619 10620 10621 10622 10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633 10634
            } 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++) {
10635 10636
            VIR_FREE(devices[i]);
            VIR_FREE(parents[i]);
10637
        }
10638
        VIR_FREE(parents);
10639 10640 10641
    } else {
        for (i = 0; i < num_devices; i++) {
            vshPrint(ctl, "%s\n", devices[i]);
10642
            VIR_FREE(devices[i]);
10643
        }
10644
    }
10645
    VIR_FREE(devices);
E
Eric Blake 已提交
10646
    return true;
10647 10648 10649 10650 10651 10652
}

/*
 * "nodedev-dumpxml" command
 */
static const vshCmdInfo info_node_device_dumpxml[] = {
10653 10654
    {"help", N_("node device details in XML")},
    {"desc", N_("Output the node device details as an XML dump to stdout.")},
10655 10656 10657 10658 10659
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_dumpxml[] = {
10660
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
10661 10662 10663
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10664
static bool
10665 10666
cmdNodeDeviceDumpXML (vshControl *ctl, const vshCmd *cmd)
{
10667
    const char *name = NULL;
10668
    virNodeDevicePtr device;
L
Laine Stump 已提交
10669
    char *xml;
10670

10671
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10672
        return false;
10673
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
10674
        return false;
10675
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
10676
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
10677
        return false;
10678 10679
    }

L
Laine Stump 已提交
10680 10681 10682
    xml = virNodeDeviceGetXMLDesc(device, 0);
    if (!xml) {
        virNodeDeviceFree(device);
E
Eric Blake 已提交
10683
        return false;
L
Laine Stump 已提交
10684 10685 10686
    }

    vshPrint(ctl, "%s\n", xml);
10687
    VIR_FREE(xml);
10688
    virNodeDeviceFree(device);
E
Eric Blake 已提交
10689
    return true;
10690 10691
}

10692 10693 10694 10695
/*
 * "nodedev-dettach" command
 */
static const vshCmdInfo info_node_device_dettach[] = {
10696 10697
    {"help", N_("dettach node device from its device driver")},
    {"desc", N_("Dettach node device from its device driver before assigning to a domain.")},
10698 10699 10700 10701 10702
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_dettach[] = {
10703
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
10704 10705 10706
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10707
static bool
10708 10709
cmdNodeDeviceDettach (vshControl *ctl, const vshCmd *cmd)
{
10710
    const char *name = NULL;
10711
    virNodeDevicePtr device;
E
Eric Blake 已提交
10712
    bool ret = true;
10713

10714
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10715
        return false;
10716
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
10717
        return false;
10718
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
10719
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
10720
        return false;
10721 10722 10723 10724 10725
    }

    if (virNodeDeviceDettach(device) == 0) {
        vshPrint(ctl, _("Device %s dettached\n"), name);
    } else {
10726
        vshError(ctl, _("Failed to dettach device %s"), name);
E
Eric Blake 已提交
10727
        ret = false;
10728 10729 10730 10731 10732 10733 10734 10735 10736
    }
    virNodeDeviceFree(device);
    return ret;
}

/*
 * "nodedev-reattach" command
 */
static const vshCmdInfo info_node_device_reattach[] = {
10737 10738
    {"help", N_("reattach node device to its device driver")},
    {"desc", N_("Reattach node device to its device driver once released by the domain.")},
10739 10740 10741 10742 10743
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_reattach[] = {
10744
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
10745 10746 10747
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10748
static bool
10749 10750
cmdNodeDeviceReAttach (vshControl *ctl, const vshCmd *cmd)
{
10751
    const char *name = NULL;
10752
    virNodeDevicePtr device;
E
Eric Blake 已提交
10753
    bool ret = true;
10754

10755
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10756
        return false;
10757
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
10758
        return false;
10759
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
10760
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
10761
        return false;
10762 10763 10764 10765 10766
    }

    if (virNodeDeviceReAttach(device) == 0) {
        vshPrint(ctl, _("Device %s re-attached\n"), name);
    } else {
10767
        vshError(ctl, _("Failed to re-attach device %s"), name);
E
Eric Blake 已提交
10768
        ret = false;
10769 10770 10771 10772 10773 10774 10775 10776 10777
    }
    virNodeDeviceFree(device);
    return ret;
}

/*
 * "nodedev-reset" command
 */
static const vshCmdInfo info_node_device_reset[] = {
10778 10779
    {"help", N_("reset node device")},
    {"desc", N_("Reset node device before or after assigning to a domain.")},
10780 10781 10782 10783 10784
    {NULL, NULL}
};


static const vshCmdOptDef opts_node_device_reset[] = {
10785
    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
10786 10787 10788
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10789
static bool
10790 10791
cmdNodeDeviceReset (vshControl *ctl, const vshCmd *cmd)
{
10792
    const char *name = NULL;
10793
    virNodeDevicePtr device;
E
Eric Blake 已提交
10794
    bool ret = true;
10795

10796
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10797
        return false;
10798
    if (vshCommandOptString(cmd, "device", &name) <= 0)
E
Eric Blake 已提交
10799
        return false;
10800
    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
10801
        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
E
Eric Blake 已提交
10802
        return false;
10803 10804 10805 10806 10807
    }

    if (virNodeDeviceReset(device) == 0) {
        vshPrint(ctl, _("Device %s reset\n"), name);
    } else {
10808
        vshError(ctl, _("Failed to reset device %s"), name);
E
Eric Blake 已提交
10809
        ret = false;
10810 10811 10812 10813 10814
    }
    virNodeDeviceFree(device);
    return ret;
}

10815
/*
E
Eric Blake 已提交
10816
 * "hostname" command
10817
 */
10818
static const vshCmdInfo info_hostname[] = {
10819
    {"help", N_("print the hypervisor hostname")},
10820
    {"desc", ""},
10821 10822 10823
    {NULL, NULL}
};

E
Eric Blake 已提交
10824
static bool
10825
cmdHostname (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
10826 10827 10828
{
    char *hostname;

10829
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10830
        return false;
10831 10832 10833

    hostname = virConnectGetHostname (ctl->conn);
    if (hostname == NULL) {
10834
        vshError(ctl, "%s", _("failed to get hostname"));
E
Eric Blake 已提交
10835
        return false;
10836 10837 10838
    }

    vshPrint (ctl, "%s\n", hostname);
10839
    VIR_FREE(hostname);
10840

E
Eric Blake 已提交
10841
    return true;
10842 10843 10844 10845 10846
}

/*
 * "uri" command
 */
10847
static const vshCmdInfo info_uri[] = {
10848
    {"help", N_("print the hypervisor canonical URI")},
10849
    {"desc", ""},
10850 10851 10852
    {NULL, NULL}
};

E
Eric Blake 已提交
10853
static bool
10854
cmdURI (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
10855 10856 10857
{
    char *uri;

10858
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10859
        return false;
10860 10861 10862

    uri = virConnectGetURI (ctl->conn);
    if (uri == NULL) {
10863
        vshError(ctl, "%s", _("failed to get URI"));
E
Eric Blake 已提交
10864
        return false;
10865 10866 10867
    }

    vshPrint (ctl, "%s\n", uri);
10868
    VIR_FREE(uri);
10869

E
Eric Blake 已提交
10870
    return true;
10871 10872
}

E
Eric Blake 已提交
10873 10874 10875 10876 10877 10878 10879 10880 10881 10882
/*
 * "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 已提交
10883
static bool
E
Eric Blake 已提交
10884 10885 10886 10887 10888
cmdSysinfo (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *sysinfo;

    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10889
        return false;
E
Eric Blake 已提交
10890 10891 10892 10893

    sysinfo = virConnectGetSysinfo (ctl->conn, 0);
    if (sysinfo == NULL) {
        vshError(ctl, "%s", _("failed to get sysinfo"));
E
Eric Blake 已提交
10894
        return false;
E
Eric Blake 已提交
10895 10896 10897 10898 10899
    }

    vshPrint (ctl, "%s", sysinfo);
    VIR_FREE(sysinfo);

E
Eric Blake 已提交
10900
    return true;
E
Eric Blake 已提交
10901 10902
}

10903 10904 10905
/*
 * "vncdisplay" command
 */
10906
static const vshCmdInfo info_vncdisplay[] = {
10907 10908
    {"help", N_("vnc display")},
    {"desc", N_("Output the IP address and port number for the VNC display.")},
10909 10910 10911
    {NULL, NULL}
};

10912
static const vshCmdOptDef opts_vncdisplay[] = {
10913
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
10914 10915 10916
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10917
static bool
10918
cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
10919 10920 10921 10922 10923
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virDomainPtr dom;
E
Eric Blake 已提交
10924
    bool ret = false;
10925 10926 10927
    int port = 0;
    char *doc;

10928
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10929
        return false;
10930

J
Jim Meyering 已提交
10931
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
10932
        return false;
10933 10934 10935 10936 10937

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

10938
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
10939
    VIR_FREE(doc);
10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954
    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) ||
10955
        STREQ((const char*)obj->stringval, "0.0.0.0")) {
10956 10957 10958 10959 10960 10961
        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 已提交
10962
    ret = true;
10963 10964 10965 10966

 cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
10967
    xmlFreeDoc(xml);
10968 10969 10970 10971 10972 10973 10974
    virDomainFree(dom);
    return ret;
}

/*
 * "ttyconsole" command
 */
10975
static const vshCmdInfo info_ttyconsole[] = {
10976 10977
    {"help", N_("tty console")},
    {"desc", N_("Output the device for the TTY console.")},
10978 10979 10980
    {NULL, NULL}
};

10981
static const vshCmdOptDef opts_ttyconsole[] = {
10982
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
10983 10984 10985
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
10986
static bool
10987
cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
10988 10989 10990 10991 10992
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virDomainPtr dom;
E
Eric Blake 已提交
10993
    bool ret = false;
10994 10995
    char *doc;

10996
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
10997
        return false;
10998

J
Jim Meyering 已提交
10999
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
11000
        return false;
11001 11002 11003 11004 11005

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

11006
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
11007
    VIR_FREE(doc);
11008 11009 11010 11011 11012 11013 11014 11015 11016
    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 已提交
11017
    ret = true;
11018 11019 11020 11021

 cleanup:
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);
11022
    xmlFreeDoc(xml);
11023 11024 11025 11026 11027 11028
    virDomainFree(dom);
    return ret;
}

/*
 * "attach-device" command
11029
 */
11030
static const vshCmdInfo info_attach_device[] = {
11031 11032
    {"help", N_("attach device from an XML file")},
    {"desc", N_("Attach device from an XML <file>.")},
11033 11034 11035
    {NULL, NULL}
};

11036
static const vshCmdOptDef opts_attach_device[] = {
11037 11038 11039
    {"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")},
11040 11041 11042
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11043
static bool
11044
cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
11045 11046
{
    virDomainPtr dom;
11047
    const char *from = NULL;
11048
    char *buffer;
W
Wen Congyang 已提交
11049
    int ret;
J
Jim Fehlig 已提交
11050
    unsigned int flags;
11051

11052
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11053
        return false;
11054

J
Jim Meyering 已提交
11055
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
11056
        return false;
11057

11058
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
11059
        virDomainFree(dom);
E
Eric Blake 已提交
11060
        return false;
11061 11062
    }

11063
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
11064
        virshReportError(ctl);
11065
        virDomainFree(dom);
E
Eric Blake 已提交
11066
        return false;
11067
    }
11068

J
Jim Fehlig 已提交
11069
    if (vshCommandOptBool(cmd, "persistent")) {
11070
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
11071
        if (virDomainIsActive(dom) == 1)
11072
           flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
11073 11074 11075 11076
        ret = virDomainAttachDeviceFlags(dom, buffer, flags);
    } else {
        ret = virDomainAttachDevice(dom, buffer);
    }
11077
    VIR_FREE(buffer);
11078 11079

    if (ret < 0) {
11080
        vshError(ctl, _("Failed to attach device from %s"), from);
11081
        virDomainFree(dom);
E
Eric Blake 已提交
11082
        return false;
11083
    } else {
J
Jim Meyering 已提交
11084
        vshPrint(ctl, "%s", _("Device attached successfully\n"));
11085 11086 11087
    }

    virDomainFree(dom);
E
Eric Blake 已提交
11088
    return true;
11089 11090 11091 11092 11093 11094
}


/*
 * "detach-device" command
 */
11095
static const vshCmdInfo info_detach_device[] = {
11096 11097
    {"help", N_("detach device from an XML file")},
    {"desc", N_("Detach device from an XML <file>")},
11098 11099 11100
    {NULL, NULL}
};

11101
static const vshCmdOptDef opts_detach_device[] = {
11102 11103 11104
    {"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")},
11105 11106 11107
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11108
static bool
11109
cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
11110 11111
{
    virDomainPtr dom;
11112
    const char *from = NULL;
11113
    char *buffer;
W
Wen Congyang 已提交
11114
    int ret;
J
Jim Fehlig 已提交
11115
    unsigned int flags;
11116

11117
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11118
        return false;
11119

J
Jim Meyering 已提交
11120
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
E
Eric Blake 已提交
11121
        return false;
11122

11123
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
11124
        virDomainFree(dom);
E
Eric Blake 已提交
11125
        return false;
11126 11127
    }

11128
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
11129
        virshReportError(ctl);
11130
        virDomainFree(dom);
E
Eric Blake 已提交
11131
        return false;
11132
    }
11133

J
Jim Fehlig 已提交
11134
    if (vshCommandOptBool(cmd, "persistent")) {
11135
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
11136
        if (virDomainIsActive(dom) == 1)
11137
           flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
11138 11139 11140 11141
        ret = virDomainDetachDeviceFlags(dom, buffer, flags);
    } else {
        ret = virDomainDetachDevice(dom, buffer);
    }
11142
    VIR_FREE(buffer);
11143 11144

    if (ret < 0) {
11145
        vshError(ctl, _("Failed to detach device from %s"), from);
11146
        virDomainFree(dom);
E
Eric Blake 已提交
11147
        return false;
11148
    } else {
J
Jim Meyering 已提交
11149
        vshPrint(ctl, "%s", _("Device detached successfully\n"));
11150 11151 11152
    }

    virDomainFree(dom);
E
Eric Blake 已提交
11153
    return true;
11154 11155
}

11156

11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169
/*
 * "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")},
11170
    {"force",  VSH_OT_BOOL, 0, N_("force device update")},
11171 11172 11173
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11174
static bool
11175 11176 11177
cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom;
11178
    const char *from = NULL;
11179
    char *buffer;
W
Wen Congyang 已提交
11180
    int ret;
11181 11182
    unsigned int flags;

11183
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
11184
        return false;
11185 11186

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

11189
    if (vshCommandOptString(cmd, "file", &from) <= 0) {
11190
        virDomainFree(dom);
E
Eric Blake 已提交
11191
        return false;
11192 11193 11194
    }

    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
11195
        virshReportError(ctl);
11196
        virDomainFree(dom);
E
Eric Blake 已提交
11197
        return false;
11198 11199 11200
    }

    if (vshCommandOptBool(cmd, "persistent")) {
11201
        flags = VIR_DOMAIN_AFFECT_CONFIG;
11202
        if (virDomainIsActive(dom) == 1)
11203
           flags |= VIR_DOMAIN_AFFECT_LIVE;
11204
    } else {
11205
        flags = VIR_DOMAIN_AFFECT_LIVE;
11206
    }
11207 11208 11209 11210

    if (vshCommandOptBool(cmd, "force"))
        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;

11211 11212 11213 11214 11215 11216
    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 已提交
11217
        return false;
11218 11219 11220 11221 11222
    } else {
        vshPrint(ctl, "%s", _("Device updated successfully\n"));
    }

    virDomainFree(dom);
E
Eric Blake 已提交
11223
    return true;
11224 11225 11226
}


11227 11228 11229
/*
 * "attach-interface" command
 */
11230
static const vshCmdInfo info_attach_interface[] = {
11231 11232
    {"help", N_("attach network interface")},
    {"desc", N_("Attach new network interface.")},
11233 11234 11235
    {NULL, NULL}
};

11236
static const vshCmdOptDef opts_attach_interface[] = {
11237 11238 11239 11240 11241 11242
    {"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")},
11243
    {"model", VSH_OT_DATA, 0, N_("model type")},
11244
    {"persistent", VSH_OT_BOOL, 0, N_("persist interface attachment")},
11245 11246
    {"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")},
11247 11248 11249
    {NULL, 0, 0, NULL}
};

11250 11251 11252
/* 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. */
11253
static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate)
11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 11264 11265 11266 11267 11268 11269 11270 11271 11272 11273 11274 11275 11276 11277 11278 11279 11280 11281 11282 11283 11284
{
    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 已提交
11285
static bool
11286
cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
11287 11288
{
    virDomainPtr dom = NULL;
11289
    const char *mac = NULL, *target = NULL, *script = NULL,
11290 11291
                *type = NULL, *source = NULL, *model = NULL,
                *inboundStr = NULL, *outboundStr = NULL;
11292
    virNetDevBandwidthRate inbound, outbound;
E
Eric Blake 已提交
11293
    int typ;
W
Wen Congyang 已提交
11294 11295
    int ret;
    bool functionReturn = false;
J
Jim Fehlig 已提交
11296
    unsigned int flags;
11297 11298
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *xml;
11299

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

J
Jim Meyering 已提交
11303
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
11304 11305
        goto cleanup;

11306
    if (vshCommandOptString(cmd, "type", &type) <= 0)
11307 11308
        goto cleanup;

11309 11310 11311 11312
    if (vshCommandOptString(cmd, "source", &source) < 0 ||
        vshCommandOptString(cmd, "target", &target) < 0 ||
        vshCommandOptString(cmd, "mac", &mac) < 0 ||
        vshCommandOptString(cmd, "script", &script) < 0 ||
11313 11314 11315
        vshCommandOptString(cmd, "model", &model) < 0 ||
        vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
11316
        vshError(ctl, "missing argument");
11317
        goto cleanup;
11318
    }
11319 11320

    /* check interface type */
11321
    if (STREQ(type, "network")) {
11322
        typ = 1;
11323
    } else if (STREQ(type, "bridge")) {
11324 11325
        typ = 2;
    } else {
E
Eric Blake 已提交
11326 11327
        vshError(ctl, _("No support for %s in command 'attach-interface'"),
                 type);
11328 11329 11330
        goto cleanup;
    }

11331 11332 11333 11334 11335 11336 11337 11338 11339 11340 11341 11342 11343 11344 11345 11346 11347 11348 11349 11350 11351 11352 11353
    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;
        }
    }

11354
    /* Make XML of interface */
11355
    virBufferAsprintf(&buf, "<interface type='%s'>\n", type);
11356

11357
    if (typ == 1)
11358
        virBufferAsprintf(&buf, "  <source network='%s'/>\n", source);
11359
    else if (typ == 2)
11360
        virBufferAsprintf(&buf, "  <source bridge='%s'/>\n", source);
11361

11362
    if (target != NULL)
11363
        virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
11364
    if (mac != NULL)
11365
        virBufferAsprintf(&buf, "  <mac address='%s'/>\n", mac);
11366
    if (script != NULL)
11367
        virBufferAsprintf(&buf, "  <script path='%s'/>\n", script);
11368
    if (model != NULL)
11369
        virBufferAsprintf(&buf, "  <model type='%s'/>\n", model);
11370

11371 11372 11373 11374 11375 11376 11377 11378 11379 11380 11381 11382 11383 11384 11385 11386 11387 11388 11389 11390 11391
    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");
    }

11392
    virBufferAddLit(&buf, "</interface>\n");
11393

11394 11395
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
W
Wen Congyang 已提交
11396
        goto cleanup;
11397 11398
    }

11399
    xml = virBufferContentAndReset(&buf);
11400

J
Jim Fehlig 已提交
11401
    if (vshCommandOptBool(cmd, "persistent")) {
11402
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
11403
        if (virDomainIsActive(dom) == 1)
11404
            flags |= VIR_DOMAIN_AFFECT_LIVE;
11405
        ret = virDomainAttachDeviceFlags(dom, xml, flags);
11406
    } else {
11407
        ret = virDomainAttachDevice(dom, xml);
11408
    }
11409

11410 11411
    VIR_FREE(xml);

J
Jim Fehlig 已提交
11412
    if (ret != 0) {
L
Laine Stump 已提交
11413
        vshError(ctl, "%s", _("Failed to attach interface"));
J
Jim Fehlig 已提交
11414 11415
    } else {
        vshPrint(ctl, "%s", _("Interface attached successfully\n"));
W
Wen Congyang 已提交
11416
        functionReturn = true;
J
Jim Fehlig 已提交
11417
    }
11418 11419 11420 11421

 cleanup:
    if (dom)
        virDomainFree(dom);
11422
    virBufferFreeAndReset(&buf);
W
Wen Congyang 已提交
11423
    return functionReturn;
11424 11425 11426 11427 11428
}

/*
 * "detach-interface" command
 */
11429
static const vshCmdInfo info_detach_interface[] = {
11430 11431
    {"help", N_("detach network interface")},
    {"desc", N_("Detach network interface.")},
11432 11433 11434
    {NULL, NULL}
};

11435
static const vshCmdOptDef opts_detach_interface[] = {
11436 11437 11438 11439
    {"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")},
11440 11441 11442
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11443
static bool
11444
cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
11445 11446 11447 11448 11449 11450 11451
{
    virDomainPtr dom = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj=NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr cur = NULL;
    xmlBufferPtr xml_buf = NULL;
11452
    const char *mac =NULL, *type = NULL;
11453
    char *doc;
11454
    char buf[64];
E
Eric Blake 已提交
11455
    int i = 0, diff_mac;
W
Wen Congyang 已提交
11456 11457
    int ret;
    int functionReturn = false;
J
Jim Fehlig 已提交
11458
    unsigned int flags;
11459

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

J
Jim Meyering 已提交
11463
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
11464 11465
        goto cleanup;

11466
    if (vshCommandOptString(cmd, "type", &type) <= 0)
11467 11468
        goto cleanup;

11469 11470
    if (vshCommandOptString(cmd, "mac", &mac) < 0) {
        vshError(ctl, "%s", _("missing option"));
11471
        goto cleanup;
11472
    }
11473 11474 11475 11476 11477

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

11478
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
11479
    VIR_FREE(doc);
11480
    if (!xml) {
11481
        vshError(ctl, "%s", _("Failed to get interface information"));
11482 11483 11484
        goto cleanup;
    }

E
Eric Blake 已提交
11485
    snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
11486 11487 11488
    obj = xmlXPathEval(BAD_CAST buf, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
11489
        vshError(ctl, _("No found interface whose type is %s"), type);
11490 11491 11492
        goto cleanup;
    }

11493 11494 11495 11496 11497 11498
    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;
    }

11499 11500 11501 11502 11503 11504 11505
    if (!mac)
        goto hit;

    /* search mac */
    for (; i < obj->nodesetval->nodeNr; i++) {
        cur = obj->nodesetval->nodeTab[i]->children;
        while (cur != NULL) {
11506 11507 11508 11509 11510
            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);
11511 11512 11513 11514 11515 11516 11517
                if (!diff_mac) {
                    goto hit;
                }
            }
            cur = cur->next;
        }
    }
11518
    vshError(ctl, _("No found interface whose MAC address is %s"), mac);
11519 11520 11521 11522 11523
    goto cleanup;

 hit:
    xml_buf = xmlBufferCreate();
    if (!xml_buf) {
11524
        vshError(ctl, "%s", _("Failed to allocate memory"));
11525 11526 11527
        goto cleanup;
    }

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

J
Jim Fehlig 已提交
11533
    if (vshCommandOptBool(cmd, "persistent")) {
11534
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
11535
        if (virDomainIsActive(dom) == 1)
11536
            flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
11537 11538 11539 11540 11541 11542 11543 11544
        ret = virDomainDetachDeviceFlags(dom,
                                         (char *)xmlBufferContent(xml_buf),
                                         flags);
    } else {
        ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
    }

    if (ret != 0) {
L
Laine Stump 已提交
11545
        vshError(ctl, "%s", _("Failed to detach interface"));
J
Jim Fehlig 已提交
11546
    } else {
J
Jim Meyering 已提交
11547
        vshPrint(ctl, "%s", _("Interface detached successfully\n"));
W
Wen Congyang 已提交
11548
        functionReturn = true;
11549
    }
11550 11551 11552 11553

 cleanup:
    if (dom)
        virDomainFree(dom);
11554
    xmlXPathFreeObject(obj);
11555
    xmlXPathFreeContext(ctxt);
11556 11557
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
W
Wen Congyang 已提交
11558
    return functionReturn;
11559 11560 11561 11562 11563
}

/*
 * "attach-disk" command
 */
11564
static const vshCmdInfo info_attach_disk[] = {
11565 11566
    {"help", N_("attach disk device")},
    {"desc", N_("Attach new disk device.")},
11567 11568 11569
    {NULL, NULL}
};

11570
static const vshCmdOptDef opts_attach_disk[] = {
11571
    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
11572 11573
    {"source",  VSH_OT_DATA, VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK,
     N_("source of disk device")},
11574 11575 11576
    {"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")},
11577
    {"cache",     VSH_OT_STRING, 0, N_("cache mode of disk device")},
11578 11579 11580
    {"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")},
11581
    {"sourcetype", VSH_OT_STRING, 0, N_("type of source (block|file)")},
11582 11583 11584
    {"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")},
11585 11586 11587
    {NULL, 0, 0, NULL}
};

11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 11625 11626 11627 11628 11629 11630 11631 11632 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734
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 已提交
11735
static bool
11736
cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
11737 11738
{
    virDomainPtr dom = NULL;
11739
    const char *source = NULL, *target = NULL, *driver = NULL,
11740 11741 11742
                *subdriver = NULL, *type = NULL, *mode = NULL,
                *cache = NULL, *serial = NULL, *straddr = NULL;
    struct DiskAddress diskAddr;
W
Wen Congyang 已提交
11743 11744
    bool isFile = false, functionReturn = false;
    int ret;
J
Jim Fehlig 已提交
11745
    unsigned int flags;
11746
    const char *stype = NULL;
11747 11748
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *xml;
11749

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

J
Jim Meyering 已提交
11753
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
11754 11755
        goto cleanup;

11756
    if (vshCommandOptString(cmd, "source", &source) <= 0)
11757
        goto cleanup;
11758 11759 11760 11761
    /* Allow empty string as a placeholder that implies no source, for
     * use in adding a cdrom drive with no disk.  */
    if (!*source)
        source = NULL;
11762

11763
    if (vshCommandOptString(cmd, "target", &target) <= 0)
11764 11765
        goto cleanup;

11766 11767 11768 11769
    if (vshCommandOptString(cmd, "driver", &driver) < 0 ||
        vshCommandOptString(cmd, "subdriver", &subdriver) < 0 ||
        vshCommandOptString(cmd, "type", &type) < 0 ||
        vshCommandOptString(cmd, "mode", &mode) < 0 ||
11770 11771 11772
        vshCommandOptString(cmd, "cache", &cache) < 0 ||
        vshCommandOptString(cmd, "serial", &serial) < 0 ||
        vshCommandOptString(cmd, "address", &straddr) < 0 ||
11773
        vshCommandOptString(cmd, "sourcetype", &stype) < 0) {
11774
        vshError(ctl, "%s", _("missing option"));
11775 11776
        goto cleanup;
    }
11777

11778 11779
    if (!stype) {
        if (driver && (STREQ(driver, "file") || STREQ(driver, "tap")))
E
Eric Blake 已提交
11780
            isFile = true;
11781
    } else if (STREQ(stype, "file")) {
E
Eric Blake 已提交
11782
        isFile = true;
11783 11784 11785
    } else if (STRNEQ(stype, "block")) {
        vshError(ctl, _("Unknown source type: '%s'"), stype);
        goto cleanup;
11786 11787 11788
    }

    if (mode) {
11789
        if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
E
Eric Blake 已提交
11790 11791
            vshError(ctl, _("No support for %s in command 'attach-disk'"),
                     mode);
11792 11793 11794 11795 11796
            goto cleanup;
        }
    }

    /* Make XML of disk */
11797
    virBufferAsprintf(&buf, "<disk type='%s'",
11798 11799
                      (isFile) ? "file" : "block");
    if (type)
11800
        virBufferAsprintf(&buf, " device='%s'", type);
11801 11802
    virBufferAddLit(&buf, ">\n");

11803
    if (driver || subdriver)
11804
        virBufferAsprintf(&buf, "  <driver");
11805 11806

    if (driver)
11807
        virBufferAsprintf(&buf, " name='%s'", driver);
11808
    if (subdriver)
11809
        virBufferAsprintf(&buf, " type='%s'", subdriver);
11810 11811
    if (cache)
        virBufferAsprintf(&buf, " cache='%s'", cache);
11812

11813
    if (driver || subdriver || cache)
11814
        virBufferAddLit(&buf, "/>\n");
11815

11816 11817 11818 11819
    if (source)
        virBufferAsprintf(&buf, "  <source %s='%s'/>\n",
                          (isFile) ? "file" : "dev",
                          source);
11820
    virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
11821
    if (mode)
11822
        virBufferAsprintf(&buf, "  <%s/>\n", mode);
11823

11824 11825 11826 11827 11828 11829 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 11869 11870 11871
    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'"
                                  " bus ='0x%02x' slot='0x%02x' function='0x%0x' />\n",
                                  diskAddr.addr.pci.domain, diskAddr.addr.pci.bus,
                                  diskAddr.addr.pci.slot, diskAddr.addr.pci.function);
            } 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;
            }
        }
    }

11872
    virBufferAddLit(&buf, "</disk>\n");
11873

11874 11875
    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
11876
        return false;
11877 11878
    }

11879
    xml = virBufferContentAndReset(&buf);
11880

J
Jim Fehlig 已提交
11881
    if (vshCommandOptBool(cmd, "persistent")) {
11882
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
11883
        if (virDomainIsActive(dom) == 1)
11884
            flags |= VIR_DOMAIN_AFFECT_LIVE;
11885
        ret = virDomainAttachDeviceFlags(dom, xml, flags);
J
Jim Fehlig 已提交
11886
    } else {
11887
        ret = virDomainAttachDevice(dom, xml);
J
Jim Fehlig 已提交
11888
    }
11889

11890 11891
    VIR_FREE(xml);

J
Jim Fehlig 已提交
11892
    if (ret != 0) {
L
Laine Stump 已提交
11893
        vshError(ctl, "%s", _("Failed to attach disk"));
J
Jim Fehlig 已提交
11894 11895
    } else {
        vshPrint(ctl, "%s", _("Disk attached successfully\n"));
W
Wen Congyang 已提交
11896
        functionReturn = true;
J
Jim Fehlig 已提交
11897
    }
11898 11899 11900 11901

 cleanup:
    if (dom)
        virDomainFree(dom);
11902
    virBufferFreeAndReset(&buf);
W
Wen Congyang 已提交
11903
    return functionReturn;
11904 11905 11906 11907 11908
}

/*
 * "detach-disk" command
 */
11909
static const vshCmdInfo info_detach_disk[] = {
11910 11911
    {"help", N_("detach disk device")},
    {"desc", N_("Detach disk device.")},
11912 11913 11914
    {NULL, NULL}
};

11915
static const vshCmdOptDef opts_detach_disk[] = {
11916 11917 11918
    {"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")},
11919 11920 11921
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
11922
static bool
11923
cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
11924 11925 11926 11927 11928 11929 11930
{
    xmlDocPtr xml = NULL;
    xmlXPathObjectPtr obj=NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlNodePtr cur = NULL;
    xmlBufferPtr xml_buf = NULL;
    virDomainPtr dom = NULL;
11931
    const char *target = NULL;
11932
    char *doc;
E
Eric Blake 已提交
11933
    int i = 0, diff_tgt;
W
Wen Congyang 已提交
11934 11935
    int ret;
    bool functionReturn = false;
J
Jim Fehlig 已提交
11936
    unsigned int flags;
11937

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

J
Jim Meyering 已提交
11941
    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
11942 11943
        goto cleanup;

11944
    if (vshCommandOptString(cmd, "target", &target) <= 0)
11945 11946 11947 11948 11949 11950
        goto cleanup;

    doc = virDomainGetXMLDesc(dom, 0);
    if (!doc)
        goto cleanup;

11951
    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
11952
    VIR_FREE(doc);
11953
    if (!xml) {
11954
        vshError(ctl, "%s", _("Failed to get disk information"));
11955 11956 11957 11958 11959 11960
        goto cleanup;
    }

    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
11961
        vshError(ctl, "%s", _("Failed to get disk information"));
11962 11963 11964 11965 11966 11967 11968
        goto cleanup;
    }

    /* search target */
    for (; i < obj->nodesetval->nodeNr; i++) {
        cur = obj->nodesetval->nodeTab[i]->children;
        while (cur != NULL) {
11969 11970 11971 11972 11973
            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);
11974 11975 11976 11977 11978 11979 11980
                if (diff_tgt) {
                    goto hit;
                }
            }
            cur = cur->next;
        }
    }
11981
    vshError(ctl, _("No found disk whose target is %s"), target);
11982 11983 11984 11985 11986
    goto cleanup;

 hit:
    xml_buf = xmlBufferCreate();
    if (!xml_buf) {
11987
        vshError(ctl, "%s", _("Failed to allocate memory"));
11988 11989 11990
        goto cleanup;
    }

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

J
Jim Fehlig 已提交
11996
    if (vshCommandOptBool(cmd, "persistent")) {
11997
        flags = VIR_DOMAIN_AFFECT_CONFIG;
J
Jim Fehlig 已提交
11998
        if (virDomainIsActive(dom) == 1)
11999
            flags |= VIR_DOMAIN_AFFECT_LIVE;
J
Jim Fehlig 已提交
12000 12001 12002 12003 12004 12005 12006 12007
        ret = virDomainDetachDeviceFlags(dom,
                                         (char *)xmlBufferContent(xml_buf),
                                         flags);
    } else {
        ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
    }

    if (ret != 0) {
L
Laine Stump 已提交
12008
        vshError(ctl, "%s", _("Failed to detach disk"));
J
Jim Fehlig 已提交
12009
    } else {
J
Jim Meyering 已提交
12010
        vshPrint(ctl, "%s", _("Disk detached successfully\n"));
W
Wen Congyang 已提交
12011
        functionReturn = true;
12012
    }
12013 12014

 cleanup:
12015
    xmlXPathFreeObject(obj);
12016
    xmlXPathFreeContext(ctxt);
12017 12018
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
12019 12020
    if (dom)
        virDomainFree(dom);
W
Wen Congyang 已提交
12021
    return functionReturn;
12022 12023
}

12024 12025 12026 12027
/*
 * "cpu-compare" command
 */
static const vshCmdInfo info_cpu_compare[] = {
12028 12029
    {"help", N_("compare host CPU with a CPU described by an XML file")},
    {"desc", N_("compare CPU with host CPU")},
12030 12031 12032 12033
    {NULL, NULL}
};

static const vshCmdOptDef opts_cpu_compare[] = {
12034
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML CPU description")},
12035 12036 12037
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12038
static bool
12039 12040
cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
{
12041
    const char *from = NULL;
12042
    bool ret = false;
12043 12044
    char *buffer;
    int result;
12045 12046 12047 12048 12049 12050
    const char *snippet;

    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlBufferPtr xml_buf = NULL;
    xmlNodePtr node;
12051

12052
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12053
        return false;
12054

12055
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
12056
        return false;
12057

12058 12059 12060
    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
        vshError(ctl, _("Failed to read file '%s' to compare"),
                 from);
E
Eric Blake 已提交
12061
        return false;
12062
    }
12063

12064 12065 12066 12067 12068 12069 12070 12071 12072 12073 12074 12075 12076 12077 12078 12079 12080 12081 12082 12083 12084 12085 12086 12087 12088
    /* 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);
12089 12090 12091 12092 12093

    switch (result) {
    case VIR_CPU_COMPARE_INCOMPATIBLE:
        vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"),
                 from);
12094
        goto cleanup;
12095 12096 12097 12098 12099 12100 12101 12102 12103 12104 12105 12106 12107 12108 12109
        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);
12110
        goto cleanup;
12111 12112
    }

12113 12114 12115 12116 12117 12118 12119 12120
    ret = true;

cleanup:
    VIR_FREE(buffer);
    xmlBufferFree(xml_buf);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);

12121 12122 12123
    return ret;
}

12124 12125 12126 12127
/*
 * "cpu-baseline" command
 */
static const vshCmdInfo info_cpu_baseline[] = {
12128 12129
    {"help", N_("compute baseline CPU")},
    {"desc", N_("Compute baseline CPU for a set of given CPUs.")},
12130 12131 12132 12133
    {NULL, NULL}
};

static const vshCmdOptDef opts_cpu_baseline[] = {
12134
    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing XML CPU descriptions")},
12135 12136 12137
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12138
static bool
12139 12140
cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
{
12141
    const char *from = NULL;
12142
    bool ret = false;
12143 12144 12145
    char *buffer;
    char *result = NULL;
    const char **list = NULL;
12146 12147 12148 12149
    int count = 0;

    xmlDocPtr xml = NULL;
    xmlNodePtr *node_list = NULL;
12150
    xmlXPathContextPtr ctxt = NULL;
12151 12152 12153
    xmlBufferPtr xml_buf = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    int i;
12154

12155
    if (!vshConnectionUsability(ctl, ctl->conn))
E
Eric Blake 已提交
12156
        return false;
12157

12158
    if (vshCommandOptString(cmd, "file", &from) <= 0)
E
Eric Blake 已提交
12159
        return false;
12160 12161

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

12164 12165 12166
    /* add an separate container around the xml */
    virBufferStrcat(&buf, "<container>", buffer, "</container>", NULL);
    if (virBufferError(&buf))
12167 12168
        goto no_memory;

12169 12170 12171 12172 12173 12174 12175 12176 12177 12178 12179 12180 12181
    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);
12182 12183 12184
        goto cleanup;
    }

12185
    list = vshCalloc(ctl, count, sizeof(const char *));
12186

12187
    if (!(xml_buf = xmlBufferCreate()))
12188 12189
        goto no_memory;

12190 12191
    for (i = 0; i < count; i++) {
        xmlBufferEmpty(xml_buf);
12192

12193 12194 12195
        if (xmlNodeDump(xml_buf, xml,  node_list[i], 0, 0) < 0) {
            vshError(ctl, _("Failed to extract <cpu> element"));
            goto cleanup;
12196 12197
        }

12198
        list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
12199 12200 12201 12202
    }

    result = virConnectBaselineCPU(ctl->conn, list, count, 0);

12203
    if (result) {
12204
        vshPrint(ctl, "%s", result);
12205 12206
        ret = true;
    }
12207 12208 12209

cleanup:
    xmlXPathFreeContext(ctxt);
12210 12211
    xmlFreeDoc(xml);
    xmlBufferFree(xml_buf);
12212 12213
    VIR_FREE(result);
    if ((list != NULL) && (count > 0)) {
12214
        for (i = 0; i < count; i++)
12215 12216 12217 12218 12219 12220 12221 12222 12223
            VIR_FREE(list[i]);
    }
    VIR_FREE(list);
    VIR_FREE(buffer);

    return ret;

no_memory:
    vshError(ctl, "%s", _("Out of memory"));
E
Eric Blake 已提交
12224
    ret = false;
12225
    goto cleanup;
12226 12227
}

12228 12229 12230 12231 12232 12233 12234 12235
/* 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;

12236
    ret = vshMalloc(ctl, PATH_MAX);
12237 12238 12239

    tmpdir = getenv ("TMPDIR");
    if (!tmpdir) tmpdir = "/tmp";
12240 12241
    snprintf (ret, PATH_MAX, "%s/virshXXXXXX.xml", tmpdir);
    fd = mkstemps(ret, 4);
12242
    if (fd == -1) {
12243
        vshError(ctl, _("mkstemps: failed to create temporary file: %s"),
12244
                 strerror(errno));
12245
        VIR_FREE(ret);
12246 12247 12248 12249
        return NULL;
    }

    if (safewrite (fd, doc, strlen (doc)) == -1) {
12250 12251
        vshError(ctl, _("write: %s: failed to write to temporary file: %s"),
                 ret, strerror(errno));
S
Stefan Berger 已提交
12252
        VIR_FORCE_CLOSE(fd);
12253
        unlink (ret);
12254
        VIR_FREE(ret);
12255 12256
        return NULL;
    }
S
Stefan Berger 已提交
12257
    if (VIR_CLOSE(fd) < 0) {
12258 12259
        vshError(ctl, _("close: %s: failed to write or close temporary file: %s"),
                 ret, strerror(errno));
12260
        unlink (ret);
12261
        VIR_FREE(ret);
12262 12263 12264 12265 12266 12267 12268 12269 12270 12271 12272 12273 12274 12275 12276
        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 已提交
12277 12278 12279 12280
    virCommandPtr cmd;
    int ret = -1;
    int outfd = STDOUT_FILENO;
    int errfd = STDERR_FILENO;
12281

12282
    editor = getenv ("VISUAL");
E
Eric Blake 已提交
12283 12284 12285 12286
    if (!editor)
        editor = getenv ("EDITOR");
    if (!editor)
        editor = "vi"; /* could be cruel & default to ed(1) here */
12287

12288 12289 12290 12291 12292
    /* 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 已提交
12293 12294
     * is why sudo scrubs it by default).  Conversely, if the editor
     * is safe, we can run it directly rather than wasting a shell.
12295
     */
E
Eric Blake 已提交
12296 12297 12298 12299 12300 12301 12302 12303 12304 12305 12306 12307
    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);
12308 12309
    }

E
Eric Blake 已提交
12310 12311 12312 12313 12314 12315 12316
    virCommandSetInputFD(cmd, STDIN_FILENO);
    virCommandSetOutputFD(cmd, &outfd);
    virCommandSetErrorFD(cmd, &errfd);
    if (virCommandRunAsync(cmd, NULL) < 0 ||
        virCommandWait(cmd, NULL) < 0) {
        virshReportError(ctl);
        goto cleanup;
12317
    }
E
Eric Blake 已提交
12318
    ret = 0;
12319

E
Eric Blake 已提交
12320 12321 12322
cleanup:
    virCommandFree(cmd);
    return ret;
12323 12324 12325 12326 12327 12328 12329 12330
}

static char *
editReadBackFile (vshControl *ctl, const char *filename)
{
    char *ret;

    if (virFileReadAll (filename, VIRSH_MAX_XML_FILE, &ret) == -1) {
12331
        vshError(ctl,
12332
                 _("%s: failed to read temporary file: %s"),
12333
                 filename, strerror(errno));
12334 12335 12336 12337 12338
        return NULL;
    }
    return ret;
}

12339

P
Paolo Bonzini 已提交
12340 12341 12342 12343
/*
 * "cd" command
 */
static const vshCmdInfo info_cd[] = {
12344 12345
    {"help", N_("change the current directory")},
    {"desc", N_("Change the current directory.")},
P
Paolo Bonzini 已提交
12346 12347 12348 12349
    {NULL, NULL}
};

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

E
Eric Blake 已提交
12354
static bool
12355
cmdCd(vshControl *ctl, const vshCmd *cmd)
P
Paolo Bonzini 已提交
12356
{
12357
    const char *dir = NULL;
12358
    char *dir_malloced = NULL;
E
Eric Blake 已提交
12359
    bool ret = true;
P
Paolo Bonzini 已提交
12360 12361

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

12366
    if (vshCommandOptString(cmd, "dir", &dir) <= 0) {
P
Paolo Bonzini 已提交
12367
        uid_t uid = geteuid();
12368
        dir = dir_malloced = virGetUserDirectory(uid);
P
Paolo Bonzini 已提交
12369 12370 12371 12372
    }
    if (!dir)
        dir = "/";

P
Phil Petty 已提交
12373
    if (chdir(dir) == -1) {
12374
        vshError(ctl, _("cd: %s: %s"), strerror(errno), dir);
E
Eric Blake 已提交
12375
        ret = false;
P
Paolo Bonzini 已提交
12376 12377
    }

12378
    VIR_FREE(dir_malloced);
P
Phil Petty 已提交
12379
    return ret;
P
Paolo Bonzini 已提交
12380 12381 12382 12383 12384 12385
}

/*
 * "pwd" command
 */
static const vshCmdInfo info_pwd[] = {
12386 12387
    {"help", N_("print the current directory")},
    {"desc", N_("Print the current directory.")},
P
Paolo Bonzini 已提交
12388 12389 12390
    {NULL, NULL}
};

E
Eric Blake 已提交
12391
static bool
P
Paolo Bonzini 已提交
12392 12393 12394
cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
    char *cwd;
12395
    bool ret = true;
P
Paolo Bonzini 已提交
12396

12397 12398
    cwd = getcwd(NULL, 0);
    if (!cwd) {
12399 12400
        vshError(ctl, _("pwd: cannot get current directory: %s"),
                 strerror(errno));
12401 12402
        ret = false;
    } else {
P
Paolo Bonzini 已提交
12403
        vshPrint (ctl, _("%s\n"), cwd);
12404 12405
        VIR_FREE(cwd);
    }
P
Paolo Bonzini 已提交
12406

12407
    return ret;
P
Paolo Bonzini 已提交
12408 12409
}

E
Eric Blake 已提交
12410 12411 12412 12413 12414 12415 12416 12417 12418 12419 12420 12421
/*
 * "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")},
12422
    {"string", VSH_OT_ARGV, 0, N_("arguments to echo")},
E
Eric Blake 已提交
12423 12424 12425 12426 12427 12428
    {NULL, 0, 0, NULL}
};

/* Exists mainly for debugging virsh, but also handy for adding back
 * quotes for later evaluation.
 */
E
Eric Blake 已提交
12429
static bool
12430
cmdEcho (vshControl *ctl, const vshCmd *cmd)
E
Eric Blake 已提交
12431 12432 12433 12434
{
    bool shell = false;
    bool xml = false;
    int count = 0;
12435
    const vshCmdOpt *opt = NULL;
E
Eric Blake 已提交
12436 12437 12438 12439 12440 12441 12442 12443
    char *arg;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

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

12444
    while ((opt = vshCommandOptArgv(cmd, opt))) {
12445 12446
        char *str;
        virBuffer xmlbuf = VIR_BUFFER_INITIALIZER;
E
Eric Blake 已提交
12447

12448
        arg = opt->data;
12449

E
Eric Blake 已提交
12450 12451
        if (count)
            virBufferAddChar(&buf, ' ');
12452

E
Eric Blake 已提交
12453
        if (xml) {
12454 12455 12456 12457
            virBufferEscapeString(&xmlbuf, "%s", arg);
            if (virBufferError(&buf)) {
                vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
                return false;
E
Eric Blake 已提交
12458
            }
12459 12460 12461
            str = virBufferContentAndReset(&xmlbuf);
        } else {
            str = vshStrdup(ctl, arg);
E
Eric Blake 已提交
12462
        }
12463 12464 12465 12466 12467

        if (shell)
            virBufferEscapeShell(&buf, str);
        else
            virBufferAdd(&buf, str, -1);
E
Eric Blake 已提交
12468
        count++;
12469
        VIR_FREE(str);
E
Eric Blake 已提交
12470 12471 12472 12473
    }

    if (virBufferError(&buf)) {
        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
E
Eric Blake 已提交
12474
        return false;
E
Eric Blake 已提交
12475 12476 12477 12478 12479
    }
    arg = virBufferContentAndReset(&buf);
    if (arg)
        vshPrint(ctl, "%s", arg);
    VIR_FREE(arg);
E
Eric Blake 已提交
12480
    return true;
E
Eric Blake 已提交
12481 12482
}

12483 12484 12485 12486
/*
 * "edit" command
 */
static const vshCmdInfo info_edit[] = {
12487 12488
    {"help", N_("edit XML configuration for a domain")},
    {"desc", N_("Edit the XML configuration for a domain.")},
12489 12490 12491 12492
    {NULL, NULL}
};

static const vshCmdOptDef opts_edit[] = {
12493
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
12494 12495 12496 12497 12498 12499
    {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 已提交
12500
static bool
12501 12502
cmdEdit (vshControl *ctl, const vshCmd *cmd)
{
E
Eric Blake 已提交
12503
    bool ret = false;
12504 12505 12506 12507 12508
    virDomainPtr dom = NULL;
    char *tmp = NULL;
    char *doc = NULL;
    char *doc_edited = NULL;
    char *doc_reread = NULL;
E
Eric Blake 已提交
12509
    unsigned int flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
12510

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

J
Jim Meyering 已提交
12514
    dom = vshCommandOptDomain (ctl, cmd, NULL);
12515 12516 12517 12518
    if (dom == NULL)
        goto cleanup;

    /* Get the XML configuration of the domain. */
12519
    doc = virDomainGetXMLDesc (dom, flags);
12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 12534 12535 12536 12537
    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 已提交
12538
        ret = true;
12539 12540 12541 12542 12543 12544 12545
        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.
     */
12546
    doc_reread = virDomainGetXMLDesc (dom, flags);
12547 12548 12549 12550
    if (!doc_reread)
        goto cleanup;

    if (STRNEQ (doc, doc_reread)) {
12551 12552
        vshError(ctl,
                 "%s", _("ERROR: the XML configuration was changed by another user"));
12553 12554 12555 12556 12557 12558 12559 12560 12561 12562 12563 12564
        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 已提交
12565
    ret = true;
12566 12567 12568 12569 12570

 cleanup:
    if (dom)
        virDomainFree (dom);

12571 12572 12573
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    VIR_FREE(doc_reread);
12574 12575 12576

    if (tmp) {
        unlink (tmp);
12577
        VIR_FREE(tmp);
12578 12579 12580 12581 12582
    }

    return ret;
}

12583

12584 12585 12586 12587
/*
 * "net-edit" command
 */
static const vshCmdInfo info_network_edit[] = {
12588 12589
    {"help", N_("edit XML configuration for a network")},
    {"desc", N_("Edit the XML configuration for a network.")},
12590 12591 12592 12593
    {NULL, NULL}
};

static const vshCmdOptDef opts_network_edit[] = {
12594
    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
12595 12596 12597 12598 12599 12600 12601 12602 12603 12604
    {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[] = {
12605 12606
    {"help", N_("edit XML configuration for a storage pool")},
    {"desc", N_("Edit the XML configuration for a storage pool.")},
12607 12608 12609 12610
    {NULL, NULL}
};

static const vshCmdOptDef opts_pool_edit[] = {
12611
    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
12612 12613 12614 12615 12616 12617
    {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 已提交
12618 12619 12620
/*
 * "quit" command
 */
12621
static const vshCmdInfo info_quit[] = {
12622
    {"help", N_("quit this interactive terminal")},
12623
    {"desc", ""},
12624
    {NULL, NULL}
K
Karel Zak 已提交
12625 12626
};

E
Eric Blake 已提交
12627
static bool
12628
cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
12629
{
E
Eric Blake 已提交
12630 12631
    ctl->imode = false;
    return true;
K
Karel Zak 已提交
12632 12633
}

12634 12635 12636 12637 12638 12639 12640
/* 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;
12641
    bool halt = false;
12642 12643 12644
    char *doc = NULL;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
E
Eric Blake 已提交
12645
    const char *name = NULL;
12646 12647

    snapshot = virDomainSnapshotCreateXML(dom, buffer, flags);
12648 12649 12650 12651 12652 12653 12654 12655 12656 12657 12658 12659 12660 12661 12662 12663 12664 12665 12666 12667 12668 12669 12670 12671

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

12672 12673 12674
    if (snapshot == NULL)
        goto cleanup;

12675 12676 12677 12678 12679
    if (halt && virDomainDestroy(dom) < 0) {
        virshReportError(ctl);
        goto cleanup;
    }

E
Eric Blake 已提交
12680
    name = virDomainSnapshotGetName(snapshot);
12681
    if (!name) {
E
Eric Blake 已提交
12682
        vshError(ctl, "%s", _("Could not get snapshot name"));
12683 12684 12685 12686 12687 12688 12689 12690 12691 12692 12693 12694 12695 12696 12697 12698 12699 12700 12701
        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;
}

12702 12703 12704 12705
/*
 * "snapshot-create" command
 */
static const vshCmdInfo info_snapshot_create[] = {
E
Eric Blake 已提交
12706 12707
    {"help", N_("Create a snapshot from XML")},
    {"desc", N_("Create a snapshot (disk and RAM) from XML")},
12708 12709 12710 12711 12712 12713
    {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")},
12714 12715 12716
    {"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")},
12717
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
12718
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
12719 12720 12721
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
12722
static bool
12723 12724 12725
cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
12726
    bool ret = false;
12727
    const char *from = NULL;
12728
    char *buffer = NULL;
12729 12730 12731 12732 12733 12734 12735 12736
    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;
12737 12738
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
12739 12740
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
12741

12742
    if (!vshConnectionUsability(ctl, ctl->conn))
12743 12744 12745 12746 12747 12748
        goto cleanup;

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

12749
    if (vshCommandOptString(cmd, "xmlfile", &from) <= 0)
E
Eric Blake 已提交
12750
        buffer = vshStrdup(ctl, "<domainsnapshot/>");
12751 12752 12753 12754 12755 12756 12757 12758 12759 12760 12761 12762 12763 12764 12765
    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;
    }

12766
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, from);
12767 12768 12769 12770 12771 12772 12773 12774 12775

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

    return ret;
}

12776 12777 12778
/*
 * "snapshot-create-as" command
 */
12779 12780 12781 12782 12783 12784 12785 12786 12787 12788 12789 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 12818 12819 12820 12821 12822 12823 12824 12825 12826 12827 12828 12829 12830 12831 12832 12833 12834
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;
}

12835 12836 12837 12838 12839 12840 12841 12842 12843 12844
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 已提交
12845
    {"print-xml", VSH_OT_BOOL, 0, N_("print XML document rather than create")},
12846
    {"no-metadata", VSH_OT_BOOL, 0, N_("take snapshot but create no metadata")},
12847
    {"halt", VSH_OT_BOOL, 0, N_("halt domain after snapshot is created")},
12848 12849 12850
    {"disk-only", VSH_OT_BOOL, 0, N_("capture disk state but not vm state")},
    {"diskspec", VSH_OT_ARGV, 0,
     N_("disk attributes: disk[,snapshot=type][,driver=type][,file=name]")},
12851 12852 12853 12854 12855 12856 12857 12858 12859 12860 12861 12862
    {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;
12863
    unsigned int flags = 0;
12864
    const vshCmdOpt *opt = NULL;
12865 12866 12867

    if (vshCommandOptBool(cmd, "no-metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
12868 12869
    if (vshCommandOptBool(cmd, "halt"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_HALT;
12870 12871
    if (vshCommandOptBool(cmd, "disk-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY;
12872 12873 12874 12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887

    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)
12888
        virBufferEscapeString(&buf, "  <name>%s</name>\n", name);
12889
    if (desc)
12890
        virBufferEscapeString(&buf, "  <description>%s</description>\n", desc);
12891 12892 12893 12894 12895 12896 12897 12898 12899 12900
    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");
    }
12901 12902 12903 12904 12905 12906 12907 12908
    virBufferAddLit(&buf, "</domainsnapshot>\n");

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

E
Eric Blake 已提交
12909 12910 12911 12912 12913 12914
    if (vshCommandOptBool(cmd, "print-xml")) {
        vshPrint(ctl, "%s\n",  buffer);
        ret = true;
        goto cleanup;
    }

12915
    ret = vshSnapshotCreate(ctl, dom, buffer, flags, NULL);
12916 12917 12918 12919 12920 12921 12922 12923 12924

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

    return ret;
}

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

12964 12965 12966 12967 12968 12969 12970 12971 12972 12973 12974
/*
 * "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")},
12975
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
12976
    {"current", VSH_OT_BOOL, 0, N_("also set edited snapshot as current")},
12977 12978
    {"rename", VSH_OT_BOOL, 0, N_("allow renaming an existing snapshot")},
    {"clone", VSH_OT_BOOL, 0, N_("allow cloning to new name")},
12979 12980 12981 12982 12983 12984 12985 12986
    {NULL, 0, 0, NULL}
};

static bool
cmdSnapshotEdit(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
    virDomainSnapshotPtr snapshot = NULL;
12987
    virDomainSnapshotPtr edited = NULL;
12988
    const char *name;
12989
    const char *edited_name;
12990 12991 12992 12993 12994 12995
    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;
12996 12997 12998 12999 13000 13001 13002 13003
    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;
    }
13004

13005 13006
    if (vshCommandOptBool(cmd, "current") &&
        vshCommandOptBool(cmd, "snapshotname"))
13007 13008 13009 13010 13011 13012 13013 13014 13015
        define_flags |= VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;

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

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

13016 13017
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", false, dom,
                          &snapshot, &name) < 0)
13018 13019 13020 13021 13022 13023 13024
        goto cleanup;

    /* Get the XML configuration of the snapshot.  */
    doc = virDomainSnapshotGetXMLDesc(snapshot, getxml_flags);
    if (!doc)
        goto cleanup;

13025 13026 13027 13028
    /* 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;

13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 13045 13046 13047 13048 13049 13050 13051 13052 13053
    /* 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.  */
13054 13055
    edited = virDomainSnapshotCreateXML(dom, doc_edited, define_flags);
    if (!edited) {
13056 13057 13058 13059
        vshError(ctl, _("Failed to update %s"), name);
        goto cleanup;
    }

13060 13061 13062 13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083
    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;
        }
    }

13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094
    ret = true;

cleanup:
    VIR_FREE(doc);
    VIR_FREE(doc_edited);
    if (tmp) {
        unlink(tmp);
        VIR_FREE(tmp);
    }
    if (snapshot)
        virDomainSnapshotFree(snapshot);
13095 13096
    if (edited)
        virDomainSnapshotFree(edited);
13097 13098 13099 13100 13101
    if (dom)
        virDomainFree(dom);
    return ret;
}

13102 13103 13104 13105
/*
 * "snapshot-current" command
 */
static const vshCmdInfo info_snapshot_current[] = {
13106 13107
    {"help", N_("Get or set the current snapshot")},
    {"desc", N_("Get or set the current snapshot")},
13108 13109 13110 13111 13112
    {NULL, NULL}
};

static const vshCmdOptDef opts_snapshot_current[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
13113
    {"name", VSH_OT_BOOL, 0, N_("list the name, rather than the full xml")},
13114 13115
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
13116 13117
    {"snapshotname", VSH_OT_DATA, 0,
     N_("name of existing snapshot to make current")},
13118 13119 13120
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13121
static bool
13122 13123 13124
cmdSnapshotCurrent(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
13125
    bool ret = false;
13126 13127
    int current;
    virDomainSnapshotPtr snapshot = NULL;
13128
    char *xml = NULL;
13129
    const char *snapshotname = NULL;
13130
    unsigned int flags = 0;
13131
    const char *domname;
13132 13133 13134

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

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

13139
    dom = vshCommandOptDomain(ctl, cmd, &domname);
13140 13141 13142
    if (dom == NULL)
        goto cleanup;

13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 13161 13162
    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;
13163 13164 13165
        /* 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;
13166 13167 13168 13169 13170 13171 13172 13173 13174
        snapshot2 = virDomainSnapshotCreateXML(dom, xml, flags);
        if (snapshot2 == NULL)
            goto cleanup;
        virDomainSnapshotFree(snapshot2);
        vshPrint(ctl, _("Snapshot %s set as current"), snapshotname);
        ret = true;
        goto cleanup;
    }

13175
    current = virDomainHasCurrentSnapshot(dom, 0);
13176 13177 13178 13179
    if (current < 0) {
        goto cleanup;
    } else if (!current) {
        vshError(ctl, _("domain '%s' has no current snapshot"), domname);
13180
        goto cleanup;
13181
    } else {
E
Eric Blake 已提交
13182
        const char *name = NULL;
13183 13184 13185 13186

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

13187
        if (vshCommandOptBool(cmd, "name")) {
E
Eric Blake 已提交
13188
            name = virDomainSnapshotGetName(snapshot);
13189 13190
            if (!name)
                goto cleanup;
E
Eric Blake 已提交
13191 13192 13193 13194
        } else {
            xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
            if (!xml)
                goto cleanup;
13195 13196 13197
        }

        vshPrint(ctl, "%s", name ? name : xml);
13198 13199
    }

E
Eric Blake 已提交
13200
    ret = true;
13201 13202

cleanup:
13203 13204
    if (!ret)
        virshReportError(ctl);
13205
    VIR_FREE(xml);
13206 13207 13208 13209 13210 13211 13212 13213
    if (snapshot)
        virDomainSnapshotFree(snapshot);
    if (dom)
        virDomainFree(dom);

    return ret;
}

13214
/* Helper function to get the name of a snapshot's parent.  Caller
13215 13216 13217 13218 13219 13220
 * 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)
13221 13222 13223 13224 13225
{
    virDomainSnapshotPtr parent = NULL;
    char *xml = NULL;
    xmlDocPtr xmldoc = NULL;
    xmlXPathContextPtr ctxt = NULL;
13226 13227 13228
    int ret = -1;

    *parent_name = NULL;
13229 13230

    /* Try new API, since it is faster. */
13231
    if (!ctl->useSnapshotOld) {
13232 13233 13234
        parent = virDomainSnapshotGetParent(snapshot, 0);
        if (parent) {
            /* API works, and virDomainSnapshotGetName will succeed */
13235 13236
            *parent_name = vshStrdup(ctl, virDomainSnapshotGetName(parent));
            ret = 0;
13237 13238 13239 13240
            goto cleanup;
        }
        if (last_error->code == VIR_ERR_NO_DOMAIN_SNAPSHOT) {
            /* API works, and we found a root with no parent */
13241
            ret = 0;
13242 13243 13244
            goto cleanup;
        }
        /* API didn't work, fall back to XML scraping. */
13245
        ctl->useSnapshotOld = true;
13246 13247 13248 13249 13250 13251 13252 13253 13254 13255
    }

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

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

13256 13257
    *parent_name = virXPathString("string(/domainsnapshot/parent/name)", ctxt);
    ret = 0;
13258 13259

cleanup:
13260 13261 13262 13263 13264 13265 13266
    if (ret < 0) {
        virshReportError(ctl);
        vshError(ctl, "%s", _("unable to determine if snapshot has parent"));
    } else {
        virFreeError(last_error);
        last_error = NULL;
    }
13267 13268 13269 13270 13271
    if (parent)
        virDomainSnapshotFree(parent);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xmldoc);
    VIR_FREE(xml);
13272
    return ret;
13273 13274
}

13275 13276 13277 13278 13279 13280 13281 13282 13283 13284 13285
/*
 * "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")},
13286
    {"parent", VSH_OT_BOOL, 0, N_("add a column showing parent snapshot")},
13287
    {"roots", VSH_OT_BOOL, 0, N_("list only snapshots without parents")},
13288
    {"leaves", VSH_OT_BOOL, 0, N_("list only snapshots without children")},
13289 13290
    {"metadata", VSH_OT_BOOL, 0,
     N_("list only snapshots that have metadata that would prevent undefine")},
13291
    {"tree", VSH_OT_BOOL, 0, N_("list snapshots in a tree")},
13292
    {"from", VSH_OT_DATA, 0, N_("limit list to children of given snapshot")},
13293 13294
    {"current", VSH_OT_BOOL, 0,
     N_("limit list to children of current snapshot")},
13295
    {"descendants", VSH_OT_BOOL, 0, N_("with --from, list all descendants")},
13296 13297 13298
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13299
static bool
13300 13301 13302
cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
13303
    bool ret = false;
13304
    unsigned int flags = 0;
13305 13306
    int parent_filter = 0; /* -1 for roots filtering, 0 for no parent
                              information needed, 1 for parent column */
13307 13308
    int numsnaps;
    char **names = NULL;
13309
    char **parents = NULL;
13310
    int actual = 0;
13311 13312 13313 13314 13315 13316
    int i;
    xmlDocPtr xml = NULL;
    xmlXPathContextPtr ctxt = NULL;
    char *doc = NULL;
    virDomainSnapshotPtr snapshot = NULL;
    char *state = NULL;
13317
    char *parent = NULL;
13318 13319
    long long creation_longlong;
    time_t creation_time_t;
13320 13321
    char timestr[100];
    struct tm time_info;
13322
    bool tree = vshCommandOptBool(cmd, "tree");
13323
    bool leaves = vshCommandOptBool(cmd, "leaves");
13324 13325
    const char *from = NULL;
    virDomainSnapshotPtr start = NULL;
13326
    int start_index = -1;
13327
    bool descendants = false;
13328

13329 13330 13331 13332 13333 13334 13335 13336 13337 13338
    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)
13339
        goto cleanup;
13340

13341
    if (vshCommandOptBool(cmd, "parent")) {
13342 13343
        if (vshCommandOptBool(cmd, "roots")) {
            vshError(ctl, "%s",
13344
                     _("--parent and --roots are mutually exclusive"));
13345 13346
            return false;
        }
13347 13348
        if (tree) {
            vshError(ctl, "%s",
13349
                     _("--parent and --tree are mutually exclusive"));
13350 13351
            return false;
        }
13352
        parent_filter = 1;
13353
    } else if (vshCommandOptBool(cmd, "roots")) {
13354 13355
        if (tree) {
            vshError(ctl, "%s",
13356
                     _("--roots and --tree are mutually exclusive"));
13357 13358
            return false;
        }
13359 13360 13361 13362
        if (from) {
            vshError(ctl, "%s",
                     _("--roots and --from are mutually exclusive"));
        }
13363 13364
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
    }
13365 13366 13367 13368 13369 13370 13371 13372
    if (leaves) {
        if (tree) {
            vshError(ctl, "%s",
                     _("--leaves and --tree are mutually exclusive"));
            return false;
        }
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_LEAVES;
    }
13373 13374 13375

    if (vshCommandOptBool(cmd, "metadata")) {
        flags |= VIR_DOMAIN_SNAPSHOT_LIST_METADATA;
13376 13377
    }

13378
    if (from) {
13379 13380
        descendants = vshCommandOptBool(cmd, "descendants");
        if (descendants || tree) {
13381 13382
            flags |= VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
        }
13383 13384
        numsnaps = ctl->useSnapshotOld ? -1 :
            virDomainSnapshotNumChildren(start, flags);
13385
        if (numsnaps < 0) {
13386 13387
            if (ctl->useSnapshotOld ||
                last_error->code == VIR_ERR_NO_SUPPORT) {
13388
                /* We can emulate --from.  */
13389
                /* XXX can we also emulate --leaves? */
13390 13391 13392 13393 13394 13395
                virFreeError(last_error);
                last_error = NULL;
                ctl->useSnapshotOld = true;
                flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
                numsnaps = virDomainSnapshotNum(dom, flags);
            }
13396 13397
        } else if (tree) {
            numsnaps++;
13398
        }
13399
    } else {
13400
        numsnaps = virDomainSnapshotNum(dom, flags);
13401 13402

        /* Fall back to simulation if --roots was unsupported. */
13403
        /* XXX can we also emulate --leaves? */
13404 13405 13406 13407 13408 13409 13410 13411
        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);
        }
13412 13413
    }

13414 13415 13416
    if (numsnaps < 0) {
        if (!last_error)
            vshError(ctl, _("missing support"));
13417
        goto cleanup;
13418
    }
13419

13420 13421 13422 13423 13424 13425 13426 13427 13428
    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\
13429
------------------------------------------------------------\n");
13430
    }
13431

13432 13433 13434 13435
    if (!numsnaps) {
        ret = true;
        goto cleanup;
    }
13436

13437 13438 13439
    if (VIR_ALLOC_N(names, numsnaps) < 0)
        goto cleanup;

13440
    if (from && !ctl->useSnapshotOld) {
13441 13442 13443 13444 13445 13446 13447 13448 13449 13450 13451 13452 13453 13454 13455 13456 13457 13458
        /* 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);
    }
13459 13460
    if (actual < 0)
        goto cleanup;
13461

E
Eric Blake 已提交
13462 13463
    qsort(&names[0], actual, sizeof(char*), namesorter);

13464 13465
    if (tree || ctl->useSnapshotOld) {
        parents = vshCalloc(ctl, sizeof(char *), actual);
13466
        for (i = (from && !ctl->useSnapshotOld); i < actual; i++) {
13467 13468 13469 13470 13471
            if (ctl->useSnapshotOld && STREQ(names[i], from)) {
                start_index = i;
                continue;
            }

13472 13473 13474 13475
            /* free up memory from previous iterations of the loop */
            if (snapshot)
                virDomainSnapshotFree(snapshot);
            snapshot = virDomainSnapshotLookupByName(dom, names[i], 0);
13476 13477
            if (!snapshot ||
                vshGetSnapshotParent(ctl, snapshot, &parents[i]) < 0) {
13478 13479 13480 13481 13482 13483
                while (--i >= 0)
                    VIR_FREE(parents[i]);
                VIR_FREE(parents);
                goto cleanup;
            }
        }
13484 13485 13486
    }
    if (tree) {
        char indentBuf[INDENT_BUFLEN];
13487 13488
        for (i = 0 ; i < actual ; i++) {
            memset(indentBuf, '\0', sizeof indentBuf);
13489
            if (ctl->useSnapshotOld ? STREQ(names[i], from) : !parents[i])
13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503
                cmdNodeListDevicesPrint(ctl,
                                        names,
                                        parents,
                                        actual,
                                        i,
                                        i,
                                        0,
                                        0,
                                        indentBuf);
        }

        ret = true;
        goto cleanup;
    } else {
13504
        if (ctl->useSnapshotOld && descendants) {
13505 13506 13507 13508 13509 13510 13511 13512 13513 13514 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 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
            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]);
13555 13556
        }

13557
        for (i = 0; i < actual; i++) {
13558 13559
            if (ctl->useSnapshotOld &&
                (descendants ? !names[i] : STRNEQ_NULLABLE(parents[i], from)))
13560 13561
                continue;

13562
            /* free up memory from previous iterations of the loop */
13563
            VIR_FREE(parent);
13564 13565 13566 13567
            VIR_FREE(state);
            if (snapshot)
                virDomainSnapshotFree(snapshot);
            xmlXPathFreeContext(ctxt);
13568
            xmlFreeDoc(xml);
13569 13570 13571 13572 13573 13574 13575 13576 13577 13578
            VIR_FREE(doc);

            snapshot = virDomainSnapshotLookupByName(dom, names[i], 0);
            if (snapshot == NULL)
                continue;

            doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
            if (!doc)
                continue;

13579
            xml = virXMLParseStringCtxt(doc, _("(domain_snapshot)"), &ctxt);
13580 13581 13582
            if (!xml)
                continue;

13583 13584 13585
            if (parent_filter) {
                parent = virXPathString("string(/domainsnapshot/parent/name)",
                                        ctxt);
13586 13587
                if (!parent && parent_filter < 0)
                    continue;
13588 13589
            }

13590 13591 13592
            state = virXPathString("string(/domainsnapshot/state)", ctxt);
            if (state == NULL)
                continue;
13593 13594
            if (virXPathLongLong("string(/domainsnapshot/creationTime)", ctxt,
                                 &creation_longlong) < 0)
13595
                continue;
13596 13597 13598 13599 13600 13601
            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);
13602 13603
            strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S %z",
                     &time_info);
13604

13605 13606 13607 13608 13609
            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);
13610 13611 13612
        }
    }

E
Eric Blake 已提交
13613
    ret = true;
13614 13615 13616

cleanup:
    /* this frees up memory from the last iteration of the loop */
13617
    VIR_FREE(parent);
13618 13619 13620
    VIR_FREE(state);
    if (snapshot)
        virDomainSnapshotFree(snapshot);
13621 13622
    if (start)
        virDomainSnapshotFree(start);
13623
    xmlXPathFreeContext(ctxt);
13624
    xmlFreeDoc(xml);
13625
    VIR_FREE(doc);
13626
    for (i = 0; i < actual; i++) {
13627
        VIR_FREE(names[i]);
13628 13629 13630
        if (parents)
            VIR_FREE(parents[i]);
    }
13631
    VIR_FREE(names);
13632
    VIR_FREE(parents);
13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 13643 13644 13645 13646 13647 13648 13649 13650
    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")},
13651 13652
    {"security-info", VSH_OT_BOOL, 0,
     N_("include security sensitive information in XML dump")},
13653 13654 13655
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13656
static bool
13657 13658 13659
cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
13660
    bool ret = false;
13661
    const char *name = NULL;
13662 13663
    virDomainSnapshotPtr snapshot = NULL;
    char *xml = NULL;
13664 13665 13666 13667
    unsigned int flags = 0;

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

13669
    if (!vshConnectionUsability(ctl, ctl->conn))
13670 13671 13672 13673 13674 13675
        goto cleanup;

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

13676
    if (vshCommandOptString(cmd, "snapshotname", &name) <= 0)
13677 13678 13679 13680 13681 13682
        goto cleanup;

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

13683
    xml = virDomainSnapshotGetXMLDesc(snapshot, flags);
13684 13685 13686
    if (!xml)
        goto cleanup;

13687
    vshPrint(ctl, "%s", xml);
13688

E
Eric Blake 已提交
13689
    ret = true;
13690 13691 13692 13693 13694 13695 13696 13697 13698 13699 13700

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

    return ret;
}

E
Eric Blake 已提交
13701 13702 13703 13704
/*
 * "snapshot-parent" command
 */
static const vshCmdInfo info_snapshot_parent[] = {
E
Eric Blake 已提交
13705
    {"help", N_("Get the name of the parent of a snapshot")},
E
Eric Blake 已提交
13706 13707 13708 13709 13710 13711
    {"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")},
13712 13713
    {"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 已提交
13714 13715 13716 13717 13718 13719 13720 13721 13722 13723 13724 13725 13726 13727 13728 13729 13730 13731 13732
    {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;

13733 13734
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
E
Eric Blake 已提交
13735 13736
        goto cleanup;

13737
    if (vshGetSnapshotParent(ctl, snapshot, &parent) < 0)
E
Eric Blake 已提交
13738
        goto cleanup;
13739 13740 13741 13742
    if (!parent) {
        vshError(ctl, _("snapshot '%s' has no parent"), name);
        goto cleanup;
    }
E
Eric Blake 已提交
13743 13744 13745 13746 13747 13748 13749 13750 13751 13752 13753 13754 13755 13756 13757

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

    ret = true;

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

    return ret;
}

13758
/*
13759
 * "snapshot-revert" command
13760
 */
13761
static const vshCmdInfo info_snapshot_revert[] = {
13762 13763 13764 13765 13766
    {"help", N_("Revert a domain to a snapshot")},
    {"desc", N_("Revert domain to snapshot")},
    {NULL, NULL}
};

13767
static const vshCmdOptDef opts_snapshot_revert[] = {
13768
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
13769 13770
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("revert to current snapshot")},
13771 13772
    {"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 已提交
13773
    {"force", VSH_OT_BOOL, 0, N_("try harder on risky reverts")},
13774 13775 13776
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13777
static bool
13778
cmdDomainSnapshotRevert(vshControl *ctl, const vshCmd *cmd)
13779 13780
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
13781
    bool ret = false;
13782
    const char *name = NULL;
13783
    virDomainSnapshotPtr snapshot = NULL;
13784
    unsigned int flags = 0;
E
Eric Blake 已提交
13785 13786
    bool force = false;
    int result;
13787 13788 13789 13790 13791

    if (vshCommandOptBool(cmd, "running"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING;
    if (vshCommandOptBool(cmd, "paused"))
        flags |= VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED;
E
Eric Blake 已提交
13792 13793 13794 13795 13796 13797
    /* 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;
13798

13799
    if (!vshConnectionUsability(ctl, ctl->conn))
13800 13801 13802 13803 13804 13805
        goto cleanup;

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

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

E
Eric Blake 已提交
13810 13811 13812 13813 13814 13815 13816 13817 13818
    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)
13819 13820
        goto cleanup;

E
Eric Blake 已提交
13821
    ret = true;
13822 13823 13824 13825 13826 13827 13828 13829 13830 13831 13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842

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")},
13843 13844
    {"snapshotname", VSH_OT_DATA, 0, N_("snapshot name")},
    {"current", VSH_OT_BOOL, 0, N_("delete current snapshot")},
13845
    {"children", VSH_OT_BOOL, 0, N_("delete snapshot and all children")},
13846 13847 13848
    {"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")},
13849 13850 13851
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13852
static bool
13853 13854 13855
cmdSnapshotDelete(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
13856
    bool ret = false;
13857
    const char *name = NULL;
13858 13859 13860
    virDomainSnapshotPtr snapshot = NULL;
    unsigned int flags = 0;

13861
    if (!vshConnectionUsability(ctl, ctl->conn))
13862 13863 13864 13865 13866 13867
        goto cleanup;

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

13868 13869
    if (vshLookupSnapshot(ctl, cmd, "snapshotname", true, dom,
                          &snapshot, &name) < 0)
13870 13871 13872 13873
        goto cleanup;

    if (vshCommandOptBool(cmd, "children"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN;
13874 13875 13876 13877
    if (vshCommandOptBool(cmd, "children-only"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY;
    if (vshCommandOptBool(cmd, "metadata"))
        flags |= VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY;
13878

13879 13880 13881
    /* 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.  */
13882
    if (virDomainSnapshotDelete(snapshot, flags) == 0) {
13883 13884 13885 13886
        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);
13887 13888
    } else {
        vshError(ctl, _("Failed to delete snapshot %s"), name);
13889
        goto cleanup;
13890
    }
13891

E
Eric Blake 已提交
13892
    ret = true;
13893 13894 13895 13896 13897 13898 13899 13900 13901 13902

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

    return ret;
}

13903 13904 13905 13906
/*
 * "qemu-monitor-command" command
 */
static const vshCmdInfo info_qemu_monitor_command[] = {
13907 13908
    {"help", N_("QEMU Monitor Command")},
    {"desc", N_("QEMU Monitor Command")},
13909 13910 13911 13912 13913
    {NULL, NULL}
};

static const vshCmdOptDef opts_qemu_monitor_command[] = {
    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
13914
    {"hmp", VSH_OT_BOOL, 0, N_("command is in human monitor protocol")},
13915
    {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")},
13916 13917 13918
    {NULL, 0, 0, NULL}
};

E
Eric Blake 已提交
13919
static bool
13920 13921 13922
cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd)
{
    virDomainPtr dom = NULL;
E
Eric Blake 已提交
13923
    bool ret = false;
13924
    char *monitor_cmd = NULL;
13925
    char *result = NULL;
13926
    unsigned int flags = 0;
13927 13928 13929
    const vshCmdOpt *opt = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    bool pad = false;
13930 13931 13932 13933 13934 13935 13936 13937

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

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

13938 13939 13940 13941 13942 13943 13944 13945
    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"));
13946 13947
        goto cleanup;
    }
13948
    monitor_cmd = virBufferContentAndReset(&buf);
13949

13950 13951 13952 13953
    if (vshCommandOptBool(cmd, "hmp"))
        flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP;

    if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0)
13954 13955 13956 13957
        goto cleanup;

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

E
Eric Blake 已提交
13958
    ret = true;
13959 13960 13961

cleanup:
    VIR_FREE(result);
13962
    VIR_FREE(monitor_cmd);
13963 13964 13965 13966 13967 13968
    if (dom)
        virDomainFree(dom);

    return ret;
}

13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 13993 13994 13995 13996 13997 13998 13999 14000 14001 14002 14003 14004 14005 14006 14007 14008 14009 14010 14011 14012 14013 14014
/*
 * "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;
}

14015
static const vshCmdDef domManagementCmds[] = {
14016 14017 14018 14019 14020 14021 14022 14023
    {"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},
    {"blkiotune", cmdBlkiotune, opts_blkiotune, info_blkiotune, 0},
14024 14025
    {"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0},
    {"blockjob", cmdBlockJob, opts_block_job, info_block_job, 0},
14026
#ifndef WIN32
14027
    {"console", cmdConsole, opts_console, info_console, 0},
14028
#endif
14029 14030 14031 14032 14033 14034 14035 14036 14037 14038 14039
    {"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},
14040
    {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink, 0},
14041 14042 14043 14044 14045 14046 14047 14048 14049 14050 14051 14052
    {"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},
14053
    {"send-key", cmdSendKey, opts_send_key, info_send_key},
14054 14055 14056 14057 14058 14059 14060 14061
    {"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},
14062 14063
    {"migrate-setspeed", cmdMigrateSetMaxSpeed,
     opts_migrate_setspeed, info_migrate_setspeed, 0},
14064 14065
    {"migrate-getspeed", cmdMigrateGetMaxSpeed,
     opts_migrate_getspeed, info_migrate_getspeed, 0},
14066
    {"reboot", cmdReboot, opts_reboot, info_reboot, 0},
X
Xu He Jie 已提交
14067
    {"reset", cmdReset, opts_reset, info_reset, 0},
14068 14069 14070
    {"restore", cmdRestore, opts_restore, info_restore, 0},
    {"resume", cmdResume, opts_resume, info_resume, 0},
    {"save", cmdSave, opts_save, info_save, 0},
14071 14072 14073 14074 14075 14076
    {"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},
14077
    {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo, 0},
14078
    {"screenshot", cmdScreenshot, opts_screenshot, info_screenshot, 0},
14079 14080 14081 14082 14083 14084 14085 14086 14087 14088 14089 14090
    {"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 已提交
14091
    {"vcpupin", cmdVcpuPin, opts_vcpupin, info_vcpupin, 0},
14092
    {"version", cmdVersion, opts_version, info_version, 0},
14093 14094
    {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay, 0},
    {NULL, NULL, NULL, NULL, 0}
14095 14096 14097
};

static const vshCmdDef domMonitoringCmds[] = {
14098
    {"domblkinfo", cmdDomblkinfo, opts_domblkinfo, info_domblkinfo, 0},
14099
    {"domblklist", cmdDomblklist, opts_domblklist, info_domblklist, 0},
14100
    {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat, 0},
14101
    {"domcontrol", cmdDomControl, opts_domcontrol, info_domcontrol, 0},
14102
    {"domif-getlink", cmdDomIfGetLink, opts_domif_getlink, info_domif_getlink, 0},
14103 14104 14105 14106 14107 14108
    {"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}
14109 14110 14111
};

static const vshCmdDef storagePoolCmds[] = {
14112
    {"find-storage-pool-sources-as", cmdPoolDiscoverSourcesAs,
14113
     opts_find_storage_pool_sources_as, info_find_storage_pool_sources_as, 0},
14114
    {"find-storage-pool-sources", cmdPoolDiscoverSources,
14115 14116 14117 14118 14119 14120 14121 14122 14123 14124 14125 14126 14127 14128 14129 14130 14131 14132 14133 14134 14135
     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}
14136 14137 14138
};

static const vshCmdDef storageVolCmds[] = {
14139 14140 14141 14142 14143 14144 14145 14146 14147 14148 14149 14150 14151 14152 14153 14154 14155 14156
    {"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}
14157
};
14158

14159
static const vshCmdDef networkCmds[] = {
14160 14161 14162 14163 14164 14165 14166 14167 14168 14169 14170 14171 14172 14173 14174 14175 14176 14177 14178
    {"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}
14179
};
14180

14181
static const vshCmdDef nodedevCmds[] = {
14182 14183 14184 14185 14186 14187 14188 14189 14190 14191 14192 14193 14194 14195 14196
    {"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}
14197 14198 14199
};

static const vshCmdDef ifaceCmds[] = {
E
Eric Blake 已提交
14200 14201 14202 14203
    {"iface-begin", cmdInterfaceBegin, opts_interface_begin,
     info_interface_begin, 0},
    {"iface-commit", cmdInterfaceCommit, opts_interface_commit,
     info_interface_commit, 0},
14204 14205 14206 14207 14208 14209 14210 14211 14212 14213 14214 14215 14216 14217
    {"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 已提交
14218 14219
    {"iface-rollback", cmdInterfaceRollback, opts_interface_rollback,
     info_interface_rollback, 0},
14220 14221 14222 14223 14224
    {"iface-start", cmdInterfaceStart, opts_interface_start,
     info_interface_start, 0},
    {"iface-undefine", cmdInterfaceUndefine, opts_interface_undefine,
     info_interface_undefine, 0},
    {NULL, NULL, NULL, NULL, 0}
14225
};
14226

14227
static const vshCmdDef nwfilterCmds[] = {
14228 14229 14230 14231 14232 14233 14234 14235 14236 14237 14238
    {"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}
14239
};
14240

14241
static const vshCmdDef secretCmds[] = {
14242 14243 14244 14245 14246 14247 14248 14249 14250 14251 14252 14253
    {"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}
14254
};
14255

14256
static const vshCmdDef virshCmds[] = {
14257 14258 14259 14260 14261 14262 14263
    {"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}
14264
};
14265

14266
static const vshCmdDef snapshotCmds[] = {
14267 14268
    {"snapshot-create", cmdSnapshotCreate, opts_snapshot_create,
     info_snapshot_create, 0},
14269 14270
    {"snapshot-create-as", cmdSnapshotCreateAs, opts_snapshot_create_as,
     info_snapshot_create_as, 0},
14271 14272 14273 14274 14275 14276
    {"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},
14277 14278
    {"snapshot-edit", cmdSnapshotEdit, opts_snapshot_edit,
     info_snapshot_edit, 0},
14279 14280
    {"snapshot-list", cmdSnapshotList, opts_snapshot_list,
     info_snapshot_list, 0},
E
Eric Blake 已提交
14281 14282
    {"snapshot-parent", cmdSnapshotParent, opts_snapshot_parent,
     info_snapshot_parent, 0},
14283 14284 14285
    {"snapshot-revert", cmdDomainSnapshotRevert, opts_snapshot_revert,
     info_snapshot_revert, 0},
    {NULL, NULL, NULL, NULL, 0}
14286
};
14287

14288
static const vshCmdDef hostAndHypervisorCmds[] = {
14289 14290 14291 14292 14293
    {"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},
14294
    {"nodecpustats", cmdNodeCpuStats, opts_node_cpustats, info_nodecpustats, 0},
14295
    {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo, 0},
14296
    {"nodememstats", cmdNodeMemStats, opts_node_memstats, info_nodememstats, 0},
14297
    {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach},
14298 14299 14300 14301 14302
    {"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},
    {NULL, NULL, NULL, NULL, 0}
K
Karel Zak 已提交
14303 14304
};

14305 14306 14307 14308 14309 14310 14311 14312 14313 14314 14315 14316 14317 14318 14319 14320 14321
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 已提交
14322 14323 14324 14325
/* ---------------
 * Utils for work with command definition
 * ---------------
 */
K
Karel Zak 已提交
14326
static const char *
14327
vshCmddefGetInfo(const vshCmdDef * cmd, const char *name)
14328
{
14329
    const vshCmdInfo *info;
14330

K
Karel Zak 已提交
14331
    for (info = cmd->info; info && info->name; info++) {
14332
        if (STREQ(info->name, name))
K
Karel Zak 已提交
14333 14334 14335 14336 14337
            return info->data;
    }
    return NULL;
}

14338
/* Validate that the options associated with cmd can be parsed.  */
14339
static int
E
Eric Blake 已提交
14340
vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg,
14341 14342 14343 14344 14345
                  uint32_t *opts_required)
{
    int i;
    bool optional = false;

E
Eric Blake 已提交
14346 14347 14348
    *opts_need_arg = 0;
    *opts_required = 0;

14349 14350 14351 14352 14353 14354 14355 14356 14357
    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 已提交
14358
            if (opt->flags & VSH_OFLAG_REQ)
14359 14360 14361
                return -1; /* bool options can't be mandatory */
            continue;
        }
E
Eric Blake 已提交
14362 14363
        if (opt->flags & VSH_OFLAG_REQ_OPT) {
            if (opt->flags & VSH_OFLAG_REQ)
L
Lai Jiangshan 已提交
14364 14365 14366 14367
                *opts_required |= 1 << i;
            continue;
        }

14368
        *opts_need_arg |= 1 << i;
E
Eric Blake 已提交
14369
        if (opt->flags & VSH_OFLAG_REQ) {
14370 14371 14372 14373 14374 14375
            if (optional)
                return -1; /* mandatory options must be listed first */
            *opts_required |= 1 << i;
        } else {
            optional = true;
        }
14376 14377 14378

        if (opt->type == VSH_OT_ARGV && cmd->opts[i + 1].name)
            return -1; /* argv option must be listed last */
14379 14380 14381 14382
    }
    return 0;
}

14383
static const vshCmdOptDef *
14384
vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
14385
                   uint32_t *opts_seen, int *opt_index)
14386
{
14387 14388 14389 14390
    int i;

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

14392
        if (STREQ(opt->name, name)) {
14393
            if ((*opts_seen & (1 << i)) && opt->type != VSH_OT_ARGV) {
14394 14395 14396
                vshError(ctl, _("option --%s already seen"), name);
                return NULL;
            }
14397 14398
            *opts_seen |= 1 << i;
            *opt_index = i;
K
Karel Zak 已提交
14399
            return opt;
14400 14401 14402 14403 14404
        }
    }

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

14408
static const vshCmdOptDef *
14409 14410
vshCmddefGetData(const vshCmdDef *cmd, uint32_t *opts_need_arg,
                 uint32_t *opts_seen)
14411
{
14412
    int i;
14413
    const vshCmdOptDef *opt;
K
Karel Zak 已提交
14414

14415 14416 14417 14418
    if (!*opts_need_arg)
        return NULL;

    /* Grab least-significant set bit */
E
Eric Blake 已提交
14419
    i = ffs(*opts_need_arg) - 1;
14420
    opt = &cmd->opts[i];
14421
    if (opt->type != VSH_OT_ARGV)
14422
        *opts_need_arg &= ~(1 << i);
14423
    *opts_seen |= 1 << i;
14424
    return opt;
K
Karel Zak 已提交
14425 14426
}

14427 14428 14429
/*
 * Checks for required options
 */
14430
static int
14431 14432
vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required,
                    uint32_t opts_seen)
14433
{
14434
    const vshCmdDef *def = cmd->def;
14435 14436 14437 14438 14439 14440 14441 14442 14443
    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];
14444

14445
            vshError(ctl,
14446
                     opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ?
14447 14448 14449
                     _("command '%s' requires <%s> option") :
                     _("command '%s' requires --%s option"),
                     def->name, opt->name);
14450 14451
        }
    }
14452
    return -1;
14453 14454
}

14455
static const vshCmdDef *
14456 14457
vshCmddefSearch(const char *cmdname)
{
14458
    const vshCmdGrp *g;
14459
    const vshCmdDef *c;
14460

14461 14462
    for (g = cmdGroups; g->name; g++) {
        for (c = g->commands; c->name; c++) {
14463
            if (STREQ(c->name, cmdname))
14464 14465 14466 14467
                return c;
        }
    }

K
Karel Zak 已提交
14468 14469 14470
    return NULL;
}

14471 14472 14473 14474 14475 14476
static const vshCmdGrp *
vshCmdGrpSearch(const char *grpname)
{
    const vshCmdGrp *g;

    for (g = cmdGroups; g->name; g++) {
14477
        if (STREQ(g->name, grpname) || STREQ(g->keyword, grpname))
14478 14479 14480 14481 14482 14483
            return g;
    }

    return NULL;
}

E
Eric Blake 已提交
14484
static bool
14485 14486 14487 14488 14489 14490 14491
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 已提交
14492
        return false;
14493 14494 14495 14496 14497 14498 14499 14500 14501 14502
    } 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 已提交
14503
    return true;
14504 14505
}

E
Eric Blake 已提交
14506
static bool
14507
vshCmddefHelp(vshControl *ctl, const char *cmdname)
14508
{
14509
    const vshCmdDef *def = vshCmddefSearch(cmdname);
14510

K
Karel Zak 已提交
14511
    if (!def) {
14512
        vshError(ctl, _("command '%s' doesn't exist"), cmdname);
E
Eric Blake 已提交
14513
        return false;
14514
    } else {
E
Eric Blake 已提交
14515 14516
        /* Don't translate desc if it is "".  */
        const char *desc = vshCmddefGetInfo(def, "desc");
E
Eric Blake 已提交
14517
        const char *help = _(vshCmddefGetInfo(def, "help"));
14518
        char buf[256];
14519 14520
        uint32_t opts_need_arg;
        uint32_t opts_required;
14521
        bool shortopt = false; /* true if 'arg' works instead of '--opt arg' */
14522 14523 14524 14525

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

14529
        fputs(_("  NAME\n"), stdout);
14530 14531
        fprintf(stdout, "    %s - %s\n", def->name, help);

14532 14533 14534 14535 14536
        fputs(_("\n  SYNOPSIS\n"), stdout);
        fprintf(stdout, "    %s", def->name);
        if (def->opts) {
            const vshCmdOptDef *opt;
            for (opt = def->opts; opt->name; opt++) {
14537
                const char *fmt = "%s";
14538 14539
                switch (opt->type) {
                case VSH_OT_BOOL:
14540
                    fmt = "[--%s]";
14541 14542
                    break;
                case VSH_OT_INT:
E
Eric Blake 已提交
14543
                    /* xgettext:c-format */
E
Eric Blake 已提交
14544
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>"
14545
                           : _("[--%s <number>]"));
14546 14547
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
14548 14549
                    break;
                case VSH_OT_STRING:
E
Eric Blake 已提交
14550 14551
                    /* xgettext:c-format */
                    fmt = _("[--%s <string>]");
14552 14553
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
14554 14555
                    break;
                case VSH_OT_DATA:
E
Eric Blake 已提交
14556
                    fmt = ((opt->flags & VSH_OFLAG_REQ) ? "<%s>" : "[<%s>]");
14557 14558
                    if (!(opt->flags & VSH_OFLAG_REQ_OPT))
                        shortopt = true;
14559 14560 14561
                    break;
                case VSH_OT_ARGV:
                    /* xgettext:c-format */
14562 14563 14564 14565 14566 14567 14568 14569
                    if (shortopt) {
                        fmt = (opt->flags & VSH_OFLAG_REQ)
                            ? _("{[--%s] <string>}...")
                            : _("[[--%s] <string>]...");
                    } else {
                        fmt = (opt->flags & VSH_OFLAG_REQ) ? _("<%s>...")
                            : _("[<%s>]...");
                    }
14570 14571
                    break;
                default:
14572
                    assert(0);
14573
                }
14574
                fputc(' ', stdout);
E
Eric Blake 已提交
14575
                fprintf(stdout, fmt, opt->name);
14576
            }
K
Karel Zak 已提交
14577
        }
14578 14579 14580
        fputc('\n', stdout);

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

K
Karel Zak 已提交
14586
        if (def->opts) {
14587
            const vshCmdOptDef *opt;
14588
            fputs(_("\n  OPTIONS\n"), stdout);
14589
            for (opt = def->opts; opt->name; opt++) {
14590 14591
                switch (opt->type) {
                case VSH_OT_BOOL:
K
Karel Zak 已提交
14592
                    snprintf(buf, sizeof(buf), "--%s", opt->name);
14593 14594
                    break;
                case VSH_OT_INT:
14595
                    snprintf(buf, sizeof(buf),
E
Eric Blake 已提交
14596
                             (opt->flags & VSH_OFLAG_REQ) ? _("[--%s] <number>")
14597
                             : _("--%s <number>"), opt->name);
14598 14599
                    break;
                case VSH_OT_STRING:
14600
                    /* OT_STRING should never be VSH_OFLAG_REQ */
14601
                    snprintf(buf, sizeof(buf), _("--%s <string>"), opt->name);
14602 14603
                    break;
                case VSH_OT_DATA:
14604 14605
                    snprintf(buf, sizeof(buf), _("[--%s] <string>"),
                             opt->name);
14606 14607
                    break;
                case VSH_OT_ARGV:
14608 14609 14610
                    snprintf(buf, sizeof(buf),
                             shortopt ? _("[--%s] <string>") : _("<%s>"),
                             opt->name);
14611
                    break;
14612 14613 14614
                default:
                    assert(0);
                }
14615

E
Eric Blake 已提交
14616
                fprintf(stdout, "    %-15s  %s\n", buf, _(opt->help));
14617
            }
K
Karel Zak 已提交
14618 14619 14620
        }
        fputc('\n', stdout);
    }
E
Eric Blake 已提交
14621
    return true;
K
Karel Zak 已提交
14622 14623 14624 14625 14626 14627
}

/* ---------------
 * Utils for work with runtime commands data
 * ---------------
 */
14628 14629 14630
static void
vshCommandOptFree(vshCmdOpt * arg)
{
K
Karel Zak 已提交
14631 14632
    vshCmdOpt *a = arg;

14633
    while (a) {
K
Karel Zak 已提交
14634
        vshCmdOpt *tmp = a;
14635

K
Karel Zak 已提交
14636 14637
        a = a->next;

14638 14639
        VIR_FREE(tmp->data);
        VIR_FREE(tmp);
K
Karel Zak 已提交
14640 14641 14642 14643
    }
}

static void
14644
vshCommandFree(vshCmd *cmd)
14645
{
K
Karel Zak 已提交
14646 14647
    vshCmd *c = cmd;

14648
    while (c) {
K
Karel Zak 已提交
14649
        vshCmd *tmp = c;
14650

K
Karel Zak 已提交
14651 14652 14653 14654
        c = c->next;

        if (tmp->opts)
            vshCommandOptFree(tmp->opts);
14655
        VIR_FREE(tmp);
K
Karel Zak 已提交
14656 14657 14658
    }
}

E
Eric Blake 已提交
14659 14660 14661 14662 14663 14664 14665 14666 14667 14668 14669
/**
 * 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 已提交
14670
 */
E
Eric Blake 已提交
14671 14672
static int
vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
14673
{
E
Eric Blake 已提交
14674 14675
    vshCmdOpt *candidate = cmd->opts;
    const vshCmdOptDef *valid = cmd->def->opts;
14676

E
Eric Blake 已提交
14677 14678 14679 14680 14681 14682 14683
    /* 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 已提交
14684
    }
E
Eric Blake 已提交
14685 14686 14687 14688 14689 14690 14691

    /* Option not present, see if command requires it.  */
    *opt = NULL;
    while (valid) {
        if (!valid->name)
            break;
        if (STREQ(name, valid->name))
E
Eric Blake 已提交
14692
            return (valid->flags & VSH_OFLAG_REQ) == 0 ? 0 : -1;
E
Eric Blake 已提交
14693 14694 14695 14696
        valid++;
    }
    /* If we got here, the name is unknown.  */
    return -2;
K
Karel Zak 已提交
14697 14698
}

E
Eric Blake 已提交
14699 14700
/**
 * vshCommandOptInt:
14701 14702 14703 14704 14705 14706 14707
 * @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 已提交
14708
 * 0 if option not found and not required (@value untouched)
14709
 * <0 in all other cases (@value untouched)
K
Karel Zak 已提交
14710 14711
 */
static int
14712
vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
14713
{
E
Eric Blake 已提交
14714 14715 14716
    vshCmdOpt *arg;
    int ret;
    int num;
14717
    char *end_p = NULL;
14718

E
Eric Blake 已提交
14719 14720 14721 14722 14723 14724 14725
    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;
14726
    }
E
Eric Blake 已提交
14727 14728 14729 14730 14731 14732 14733

    num = strtol(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
K
Karel Zak 已提交
14734 14735
}

14736

E
Eric Blake 已提交
14737 14738 14739 14740 14741 14742
/**
 * vshCommandOptUInt:
 * @cmd command reference
 * @name option name
 * @value result
 *
14743 14744 14745 14746 14747 14748
 * Convert option to unsigned int
 * See vshCommandOptInt()
 */
static int
vshCommandOptUInt(const vshCmd *cmd, const char *name, unsigned int *value)
{
E
Eric Blake 已提交
14749 14750 14751
    vshCmdOpt *arg;
    int ret;
    unsigned int num;
14752 14753
    char *end_p = NULL;

E
Eric Blake 已提交
14754 14755 14756 14757 14758 14759 14760
    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;
14761
    }
E
Eric Blake 已提交
14762 14763 14764 14765 14766 14767 14768

    num = strtoul(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
14769 14770 14771
}


14772
/*
E
Eric Blake 已提交
14773 14774 14775 14776 14777
 * vshCommandOptUL:
 * @cmd command reference
 * @name option name
 * @value result
 *
14778 14779 14780 14781 14782
 * Convert option to unsigned long
 * See vshCommandOptInt()
 */
static int
vshCommandOptUL(const vshCmd *cmd, const char *name, unsigned long *value)
14783
{
E
Eric Blake 已提交
14784 14785
    vshCmdOpt *arg;
    int ret;
14786
    unsigned long num;
14787 14788
    char *end_p = NULL;

E
Eric Blake 已提交
14789 14790 14791 14792 14793 14794 14795
    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;
14796
    }
E
Eric Blake 已提交
14797 14798 14799 14800 14801 14802 14803

    num = strtoul(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
14804 14805
}

E
Eric Blake 已提交
14806 14807 14808 14809 14810 14811
/**
 * vshCommandOptString:
 * @cmd command reference
 * @name option name
 * @value result
 *
K
Karel Zak 已提交
14812
 * Returns option as STRING
E
Eric Blake 已提交
14813 14814 14815 14816
 * 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 已提交
14817
 */
14818 14819
static int
vshCommandOptString(const vshCmd *cmd, const char *name, const char **value)
14820
{
E
Eric Blake 已提交
14821 14822 14823 14824 14825 14826 14827 14828 14829 14830
    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;
14831
    }
14832

E
Eric Blake 已提交
14833
    if (!*arg->data && !(arg->def->flags & VSH_OFLAG_EMPTY_OK)) {
E
Eric Blake 已提交
14834 14835 14836 14837
        return -1;
    }
    *value = arg->data;
    return 1;
K
Karel Zak 已提交
14838 14839
}

E
Eric Blake 已提交
14840 14841 14842 14843 14844 14845
/**
 * vshCommandOptLongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
14846
 * Returns option as long long
14847
 * See vshCommandOptInt()
14848
 */
14849 14850 14851
static int
vshCommandOptLongLong(const vshCmd *cmd, const char *name,
                      long long *value)
14852
{
E
Eric Blake 已提交
14853 14854
    vshCmdOpt *arg;
    int ret;
14855
    long long num;
14856 14857
    char *end_p = NULL;

E
Eric Blake 已提交
14858 14859 14860 14861 14862 14863 14864
    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;
14865
    }
E
Eric Blake 已提交
14866 14867 14868 14869 14870 14871 14872

    num = strtoll(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
14873 14874
}

E
Eric Blake 已提交
14875 14876 14877 14878 14879 14880 14881 14882 14883
/**
 * vshCommandOptULongLong:
 * @cmd command reference
 * @name option name
 * @value result
 *
 * Returns option as long long
 * See vshCommandOptInt()
 */
14884 14885 14886 14887
static int
vshCommandOptULongLong(const vshCmd *cmd, const char *name,
                       unsigned long long *value)
{
E
Eric Blake 已提交
14888 14889
    vshCmdOpt *arg;
    int ret;
14890 14891 14892
    unsigned long long num;
    char *end_p = NULL;

E
Eric Blake 已提交
14893 14894 14895 14896 14897 14898 14899
    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;
14900
    }
E
Eric Blake 已提交
14901 14902 14903 14904 14905 14906 14907

    num = strtoull(arg->data, &end_p, 10);
    if ((arg->data != end_p) && (*end_p == 0)) {
        *value = num;
        return 1;
    }
    return -1;
14908 14909 14910
}


E
Eric Blake 已提交
14911 14912 14913 14914 14915 14916 14917 14918 14919
/**
 * 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 已提交
14920
 */
E
Eric Blake 已提交
14921
static bool
14922
vshCommandOptBool(const vshCmd *cmd, const char *name)
14923
{
E
Eric Blake 已提交
14924 14925 14926
    vshCmdOpt *dummy;

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

E
Eric Blake 已提交
14929 14930 14931 14932 14933
/**
 * vshCommandOptArgv:
 * @cmd command reference
 * @opt starting point for the search
 *
14934 14935
 * Returns the next argv argument after OPT (or the first one if OPT
 * is NULL), or NULL if no more are present.
14936
 *
14937
 * Requires that a VSH_OT_ARGV option be last in the
14938 14939
 * list of supported options in CMD->def->opts.
 */
14940 14941
static const vshCmdOpt *
vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt)
14942
{
14943
    opt = opt ? opt->next : cmd->opts;
14944 14945

    while (opt) {
E
Eric Blake 已提交
14946
        if (opt->def->type == VSH_OT_ARGV) {
14947
            return opt;
14948 14949 14950 14951 14952 14953
        }
        opt = opt->next;
    }
    return NULL;
}

J
Jim Meyering 已提交
14954 14955 14956 14957 14958 14959 14960 14961 14962 14963 14964 14965 14966 14967 14968 14969 14970 14971
/* 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)
14972
        vshError(ctl, _("internal error: virsh %s: no %s VSH_OT_DATA option"),
J
Jim Meyering 已提交
14973 14974 14975
                 cmd->def->name, optname);
    return found;
}
14976

K
Karel Zak 已提交
14977
static virDomainPtr
J
Jim Meyering 已提交
14978
vshCommandOptDomainBy(vshControl *ctl, const vshCmd *cmd,
14979
                      const char **name, int flag)
14980
{
K
Karel Zak 已提交
14981
    virDomainPtr dom = NULL;
14982
    const char *n = NULL;
K
Karel Zak 已提交
14983
    int id;
J
Jim Meyering 已提交
14984 14985 14986
    const char *optname = "domain";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;
14987

14988
    if (vshCommandOptString(cmd, optname, &n) <= 0)
14989 14990
        return NULL;

14991
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
14992 14993
             cmd->def->name, optname, n);

K
Karel Zak 已提交
14994 14995
    if (name)
        *name = n;
14996

K
Karel Zak 已提交
14997
    /* try it by ID */
14998
    if (flag & VSH_BYID) {
14999
        if (virStrToLong_i(n, NULL, 10, &id) == 0 && id >= 0) {
15000 15001
            vshDebug(ctl, VSH_ERR_DEBUG,
                     "%s: <%s> seems like domain ID\n",
K
Karel Zak 已提交
15002 15003 15004
                     cmd->def->name, optname);
            dom = virDomainLookupByID(ctl->conn, id);
        }
15005
    }
K
Karel Zak 已提交
15006
    /* try it by UUID */
15007
    if (dom==NULL && (flag & VSH_BYUUID) && strlen(n)==VIR_UUID_STRING_BUFLEN-1) {
15008
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain UUID\n",
15009
                 cmd->def->name, optname);
K
Karel Zak 已提交
15010
        dom = virDomainLookupByUUIDString(ctl->conn, n);
K
Karel Zak 已提交
15011
    }
K
Karel Zak 已提交
15012
    /* try it by NAME */
15013
    if (dom==NULL && (flag & VSH_BYNAME)) {
15014
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as domain NAME\n",
15015
                 cmd->def->name, optname);
K
Karel Zak 已提交
15016
        dom = virDomainLookupByName(ctl->conn, n);
15017
    }
K
Karel Zak 已提交
15018

15019
    if (!dom)
15020
        vshError(ctl, _("failed to get domain '%s'"), n);
15021

K
Karel Zak 已提交
15022 15023 15024
    return dom;
}

15025
static virNetworkPtr
J
Jim Meyering 已提交
15026
vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
15027
                       const char **name, int flag)
15028 15029
{
    virNetworkPtr network = NULL;
15030
    const char *n = NULL;
J
Jim Meyering 已提交
15031 15032 15033
    const char *optname = "network";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;
15034

15035
    if (vshCommandOptString(cmd, optname, &n) <= 0)
15036 15037
        return NULL;

15038
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
15039 15040 15041 15042 15043 15044
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
15045
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
15046
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
15047
                 cmd->def->name, optname);
15048 15049 15050 15051
        network = virNetworkLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
    if (network==NULL && (flag & VSH_BYNAME)) {
15052
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network NAME\n",
15053 15054 15055 15056 15057
                 cmd->def->name, optname);
        network = virNetworkLookupByName(ctl->conn, n);
    }

    if (!network)
15058
        vshError(ctl, _("failed to get network '%s'"), n);
15059 15060 15061 15062

    return network;
}

15063 15064 15065

static virNWFilterPtr
vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd,
15066
                        const char **name, int flag)
15067 15068
{
    virNWFilterPtr nwfilter = NULL;
15069
    const char *n = NULL;
15070 15071 15072 15073
    const char *optname = "nwfilter";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;

15074
    if (vshCommandOptString(cmd, optname, &n) <= 0)
15075 15076
        return NULL;

15077
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
15078 15079 15080 15081 15082 15083 15084
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
15085
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as nwfilter UUID\n",
15086 15087 15088 15089 15090
                 cmd->def->name, optname);
        nwfilter = virNWFilterLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
    if (nwfilter == NULL && (flag & VSH_BYNAME)) {
15091
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as nwfilter NAME\n",
15092 15093 15094 15095 15096 15097 15098 15099 15100 15101
                 cmd->def->name, optname);
        nwfilter = virNWFilterLookupByName(ctl->conn, n);
    }

    if (!nwfilter)
        vshError(ctl, _("failed to get nwfilter '%s'"), n);

    return nwfilter;
}

15102 15103
static virInterfacePtr
vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd,
15104
                         const char **name, int flag)
15105 15106
{
    virInterfacePtr iface = NULL;
15107
    const char *n = NULL;
15108 15109 15110 15111
    const char *optname = "interface";
    if (!cmd_has_option (ctl, cmd, optname))
        return NULL;

15112
    if (vshCommandOptString(cmd, optname, &n) <= 0)
15113 15114
        return NULL;

15115
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
15116 15117 15118 15119 15120 15121
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by NAME */
15122
    if ((flag & VSH_BYNAME)) {
15123
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface NAME\n",
15124 15125 15126 15127 15128
                 cmd->def->name, optname);
        iface = virInterfaceLookupByName(ctl->conn, n);
    }
    /* try it by MAC */
    if ((iface == NULL) && (flag & VSH_BYMAC)) {
15129
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface MAC\n",
15130 15131 15132 15133 15134
                 cmd->def->name, optname);
        iface = virInterfaceLookupByMACString(ctl->conn, n);
    }

    if (!iface)
15135
        vshError(ctl, _("failed to get interface '%s'"), n);
15136 15137 15138 15139

    return iface;
}

15140
static virStoragePoolPtr
15141
vshCommandOptPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname,
15142
                    const char **name, int flag)
15143 15144
{
    virStoragePoolPtr pool = NULL;
15145
    const char *n = NULL;
15146

15147
    if (vshCommandOptString(cmd, optname, &n) <= 0)
15148 15149
        return NULL;

15150
    vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
15151 15152 15153 15154 15155 15156
             cmd->def->name, optname, n);

    if (name)
        *name = n;

    /* try it by UUID */
15157
    if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) {
15158
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool UUID\n",
15159
                 cmd->def->name, optname);
15160 15161 15162
        pool = virStoragePoolLookupByUUIDString(ctl->conn, n);
    }
    /* try it by NAME */
15163
    if (pool == NULL && (flag & VSH_BYNAME)) {
15164
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool NAME\n",
15165 15166 15167 15168 15169
                 cmd->def->name, optname);
        pool = virStoragePoolLookupByName(ctl->conn, n);
    }

    if (!pool)
15170
        vshError(ctl, _("failed to get pool '%s'"), n);
15171 15172 15173 15174 15175

    return pool;
}

static virStorageVolPtr
15176
vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd,
15177 15178
                   const char *optname,
                   const char *pooloptname,
15179
                   const char **name, int flag)
15180 15181 15182
{
    virStorageVolPtr vol = NULL;
    virStoragePoolPtr pool = NULL;
15183
    const char *n = NULL, *p = NULL;
15184

15185
    if (vshCommandOptString(cmd, optname, &n) <= 0)
15186 15187
        return NULL;

15188
    if (pooloptname != NULL && vshCommandOptString(cmd, pooloptname, &p) < 0) {
15189
        vshError(ctl, "%s", _("missing option"));
15190
        return NULL;
15191
    }
15192 15193 15194 15195

    if (p)
        pool = vshCommandOptPoolBy(ctl, cmd, pooloptname, name, flag);

15196
    vshDebug(ctl, VSH_ERR_DEBUG, "%s: found option <%s>: %s\n",
15197 15198 15199 15200 15201
             cmd->def->name, optname, n);

    if (name)
        *name = n;

15202
    /* try it by name */
15203
    if (pool && (flag & VSH_BYNAME)) {
15204
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol name\n",
15205 15206 15207
                 cmd->def->name, optname);
        vol = virStorageVolLookupByName(pool, n);
    }
15208
    /* try it by key */
15209
    if (vol == NULL && (flag & VSH_BYUUID)) {
15210
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol key\n",
15211 15212 15213
                 cmd->def->name, optname);
        vol = virStorageVolLookupByKey(ctl->conn, n);
    }
15214
    /* try it by path */
15215
    if (vol == NULL && (flag & VSH_BYUUID)) {
15216
        vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as vol path\n",
15217 15218 15219 15220
                 cmd->def->name, optname);
        vol = virStorageVolLookupByPath(ctl->conn, n);
    }

15221 15222 15223 15224
    if (!vol) {
        if (pool)
            vshError(ctl, _("failed to get vol '%s'"), n);
        else
15225 15226
            vshError(ctl, _("failed to get vol '%s', specifying --%s "
                            "might help"), n, pooloptname);
15227
    }
15228 15229 15230 15231 15232 15233 15234

    if (pool)
        virStoragePoolFree(pool);

    return vol;
}

15235
static virSecretPtr
15236
vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, const char **name)
15237 15238
{
    virSecretPtr secret = NULL;
15239
    const char *n = NULL;
15240 15241 15242 15243 15244
    const char *optname = "secret";

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

15245
    if (vshCommandOptString(cmd, optname, &n) <= 0)
15246 15247
        return NULL;

15248 15249
    vshDebug(ctl, VSH_ERR_DEBUG,
             "%s: found option <%s>: %s\n", cmd->def->name, optname, n);
15250 15251 15252 15253 15254 15255 15256

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

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

    if (secret == NULL)
15257
        vshError(ctl, _("failed to get secret '%s'"), n);
15258 15259 15260 15261

    return secret;
}

K
Karel Zak 已提交
15262 15263 15264
/*
 * Executes command(s) and returns return code from last command
 */
E
Eric Blake 已提交
15265
static bool
15266
vshCommandRun(vshControl *ctl, const vshCmd *cmd)
15267
{
E
Eric Blake 已提交
15268
    bool ret = true;
15269 15270

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

15274 15275
        if ((ctl->conn == NULL || disconnected) &&
            !(cmd->def->flags & VSH_CMD_FLAG_NOCONNECT))
15276 15277
            vshReconnect(ctl);

15278
        if (enable_timing)
K
Karel Zak 已提交
15279
            GETTIMEOFDAY(&before);
15280

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

15283
        if (enable_timing)
K
Karel Zak 已提交
15284
            GETTIMEOFDAY(&after);
15285

15286
        if (!ret)
J
John Levon 已提交
15287 15288
            virshReportError(ctl);

15289
        /* try to automatically catch disconnections */
15290
        if (!ret &&
15291 15292 15293 15294 15295 15296 15297 15298 15299
            ((disconnected != 0) ||
             ((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)))))
            vshReconnect(ctl);

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

15303
        if (enable_timing)
15304
            vshPrint(ctl, _("\n(Time: %.3f ms)\n\n"),
15305 15306
                     DIFF_MSEC(&after, &before));
        else
K
Karel Zak 已提交
15307
            vshPrintExtra(ctl, "\n");
K
Karel Zak 已提交
15308 15309 15310 15311 15312 15313
        cmd = cmd->next;
    }
    return ret;
}

/* ---------------
15314
 * Command parsing
K
Karel Zak 已提交
15315 15316 15317
 * ---------------
 */

15318 15319 15320 15321 15322 15323 15324 15325 15326 15327
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 已提交
15328
    /* vshCommandStringGetArg() */
15329
    char *pos;
L
Lai Jiangshan 已提交
15330 15331 15332
    /* vshCommandArgvGetArg() */
    char **arg_pos;
    char **arg_end;
15333 15334
} vshCommandParser;

E
Eric Blake 已提交
15335
static bool
15336
vshCommandParse(vshControl *ctl, vshCommandParser *parser)
15337
{
K
Karel Zak 已提交
15338 15339 15340
    char *tkdata = NULL;
    vshCmd *clast = NULL;
    vshCmdOpt *first = NULL;
15341

K
Karel Zak 已提交
15342 15343 15344 15345
    if (ctl->cmd) {
        vshCommandFree(ctl->cmd);
        ctl->cmd = NULL;
    }
15346

15347
    while (1) {
K
Karel Zak 已提交
15348
        vshCmdOpt *last = NULL;
15349
        const vshCmdDef *cmd = NULL;
15350
        vshCommandToken tk;
L
Lai Jiangshan 已提交
15351
        bool data_only = false;
15352 15353 15354
        uint32_t opts_need_arg = 0;
        uint32_t opts_required = 0;
        uint32_t opts_seen = 0;
15355

K
Karel Zak 已提交
15356
        first = NULL;
15357

15358
        while (1) {
15359
            const vshCmdOptDef *opt = NULL;
15360

K
Karel Zak 已提交
15361
            tkdata = NULL;
15362
            tk = parser->getNextArg(ctl, parser, &tkdata);
15363 15364

            if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
15365
                goto syntaxError;
H
Hu Tao 已提交
15366 15367
            if (tk != VSH_TK_ARG) {
                VIR_FREE(tkdata);
15368
                break;
H
Hu Tao 已提交
15369
            }
15370 15371

            if (cmd == NULL) {
K
Karel Zak 已提交
15372 15373
                /* first token must be command name */
                if (!(cmd = vshCmddefSearch(tkdata))) {
15374
                    vshError(ctl, _("unknown command: '%s'"), tkdata);
15375
                    goto syntaxError;   /* ... or ignore this command only? */
K
Karel Zak 已提交
15376
                }
15377 15378 15379 15380 15381 15382 15383
                if (vshCmddefOptParse(cmd, &opts_need_arg,
                                      &opts_required) < 0) {
                    vshError(ctl,
                             _("internal error: bad options in command: '%s'"),
                             tkdata);
                    goto syntaxError;
                }
15384
                VIR_FREE(tkdata);
L
Lai Jiangshan 已提交
15385 15386 15387 15388
            } else if (data_only) {
                goto get_data;
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       c_isalnum(tkdata[2])) {
15389
                char *optstr = strchr(tkdata + 2, '=');
15390 15391
                int opt_index;

15392 15393 15394 15395
                if (optstr) {
                    *optstr = '\0'; /* convert the '=' to '\0' */
                    optstr = vshStrdup(ctl, optstr + 1);
                }
15396
                if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2,
15397
                                               &opts_seen, &opt_index))) {
15398
                    VIR_FREE(optstr);
K
Karel Zak 已提交
15399 15400
                    goto syntaxError;
                }
15401
                VIR_FREE(tkdata);
K
Karel Zak 已提交
15402 15403 15404

                if (opt->type != VSH_OT_BOOL) {
                    /* option data */
15405 15406 15407
                    if (optstr)
                        tkdata = optstr;
                    else
15408
                        tk = parser->getNextArg(ctl, parser, &tkdata);
15409
                    if (tk == VSH_TK_ERROR)
K
Karel Zak 已提交
15410
                        goto syntaxError;
15411
                    if (tk != VSH_TK_ARG) {
15412
                        vshError(ctl,
15413
                                 _("expected syntax: --%s <%s>"),
15414 15415
                                 opt->name,
                                 opt->type ==
15416
                                 VSH_OT_INT ? _("number") : _("string"));
K
Karel Zak 已提交
15417 15418
                        goto syntaxError;
                    }
15419 15420
                    if (opt->type != VSH_OT_ARGV)
                        opts_need_arg &= ~(1 << opt_index);
15421 15422 15423 15424 15425 15426 15427 15428
                } else {
                    tkdata = NULL;
                    if (optstr) {
                        vshError(ctl, _("invalid '=' after option --%s"),
                                opt->name);
                        VIR_FREE(optstr);
                        goto syntaxError;
                    }
K
Karel Zak 已提交
15429
                }
L
Lai Jiangshan 已提交
15430 15431 15432 15433
            } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
                       tkdata[2] == '\0') {
                data_only = true;
                continue;
15434
            } else {
L
Lai Jiangshan 已提交
15435
get_data:
15436 15437
                if (!(opt = vshCmddefGetData(cmd, &opts_need_arg,
                                             &opts_seen))) {
15438
                    vshError(ctl, _("unexpected data '%s'"), tkdata);
K
Karel Zak 已提交
15439 15440 15441 15442 15443
                    goto syntaxError;
                }
            }
            if (opt) {
                /* save option */
15444
                vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt));
15445

K
Karel Zak 已提交
15446 15447 15448 15449
                arg->def = opt;
                arg->data = tkdata;
                arg->next = NULL;
                tkdata = NULL;
15450

K
Karel Zak 已提交
15451 15452 15453 15454 15455
                if (!first)
                    first = arg;
                if (last)
                    last->next = arg;
                last = arg;
15456

15457
                vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n",
15458 15459
                         cmd->name,
                         opt->name,
15460 15461
                         opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"),
                         opt->type != VSH_OT_BOOL ? arg->data : _("(none)"));
K
Karel Zak 已提交
15462 15463
            }
        }
15464

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

K
Karel Zak 已提交
15469 15470 15471 15472
            c->opts = first;
            c->def = cmd;
            c->next = NULL;

15473
            if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) {
15474
                VIR_FREE(c);
15475
                goto syntaxError;
15476
            }
15477

K
Karel Zak 已提交
15478 15479 15480 15481 15482 15483
            if (!ctl->cmd)
                ctl->cmd = c;
            if (clast)
                clast->next = c;
            clast = c;
        }
15484 15485 15486

        if (tk == VSH_TK_END)
            break;
K
Karel Zak 已提交
15487
    }
15488

E
Eric Blake 已提交
15489
    return true;
K
Karel Zak 已提交
15490

15491
 syntaxError:
15492
    if (ctl->cmd) {
K
Karel Zak 已提交
15493
        vshCommandFree(ctl->cmd);
15494 15495
        ctl->cmd = NULL;
    }
K
Karel Zak 已提交
15496 15497
    if (first)
        vshCommandOptFree(first);
15498
    VIR_FREE(tkdata);
E
Eric Blake 已提交
15499
    return false;
K
Karel Zak 已提交
15500 15501
}

15502 15503 15504 15505 15506 15507 15508 15509 15510 15511 15512 15513 15514 15515 15516 15517 15518 15519
/* --------------------
 * 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 已提交
15520 15521
static bool
vshCommandArgvParse(vshControl *ctl, int nargs, char **argv)
15522 15523 15524 15525
{
    vshCommandParser parser;

    if (nargs <= 0)
E
Eric Blake 已提交
15526
        return false;
15527 15528 15529 15530 15531 15532 15533 15534 15535 15536 15537 15538 15539 15540 15541 15542 15543 15544 15545 15546 15547 15548 15549 15550 15551 15552 15553 15554 15555 15556 15557 15558 15559 15560 15561 15562 15563 15564 15565 15566 15567 15568 15569 15570 15571 15572 15573 15574 15575 15576 15577 15578 15579 15580 15581 15582 15583 15584 15585 15586 15587 15588 15589 15590 15591 15592 15593 15594 15595 15596 15597 15598

    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 已提交
15599 15600
static bool
vshCommandStringParse(vshControl *ctl, char *cmdstr)
15601 15602 15603 15604
{
    vshCommandParser parser;

    if (cmdstr == NULL || *cmdstr == '\0')
E
Eric Blake 已提交
15605
        return false;
15606 15607 15608 15609 15610 15611

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

K
Karel Zak 已提交
15612
/* ---------------
15613
 * Misc utils
K
Karel Zak 已提交
15614 15615
 * ---------------
 */
15616 15617 15618 15619 15620 15621 15622 15623 15624 15625 15626 15627 15628 15629 15630 15631 15632 15633 15634 15635 15636 15637 15638 15639 15640 15641 15642 15643
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 已提交
15644
static const char *
15645 15646
vshDomainStateToString(int state)
{
15647 15648
    /* Can't use virDomainStateTypeToString, because we want to mark
     * strings for translation.  */
15649
    switch ((virDomainState) state) {
15650
    case VIR_DOMAIN_RUNNING:
15651
        return N_("running");
15652
    case VIR_DOMAIN_BLOCKED:
15653
        return N_("idle");
15654
    case VIR_DOMAIN_PAUSED:
15655
        return N_("paused");
15656
    case VIR_DOMAIN_SHUTDOWN:
15657
        return N_("in shutdown");
15658
    case VIR_DOMAIN_SHUTOFF:
15659
        return N_("shut off");
15660
    case VIR_DOMAIN_CRASHED:
15661
        return N_("crashed");
15662
    case VIR_DOMAIN_NOSTATE:
15663
    default:
15664
        ;/*FALLTHROUGH*/
K
Karel Zak 已提交
15665
    }
15666
    return N_("no state");  /* = dom0 state */
K
Karel Zak 已提交
15667 15668
}

15669 15670 15671 15672 15673 15674 15675 15676 15677 15678 15679 15680 15681 15682 15683 15684 15685 15686 15687 15688 15689 15690 15691 15692 15693 15694 15695 15696 15697 15698 15699 15700 15701 15702 15703 15704 15705 15706 15707 15708 15709 15710 15711 15712 15713 15714 15715 15716 15717 15718 15719 15720 15721 15722 15723
static const char *
vshDomainStateReasonToString(int state, int reason)
{
    switch ((virDomainState) state) {
    case VIR_DOMAIN_NOSTATE:
        switch ((virDomainNostateReason) reason) {
        case VIR_DOMAIN_NOSTATE_UNKNOWN:
            ;
        }
        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:
            ;
        }
        break;

    case VIR_DOMAIN_BLOCKED:
        switch ((virDomainBlockedReason) reason) {
        case VIR_DOMAIN_BLOCKED_UNKNOWN:
            ;
        }
        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");
15724 15725
        case VIR_DOMAIN_PAUSED_SHUTTING_DOWN:
            return N_("shutting down");
15726 15727 15728 15729 15730 15731 15732 15733 15734 15735 15736 15737 15738 15739 15740 15741 15742 15743 15744 15745 15746 15747 15748 15749 15750 15751 15752 15753 15754 15755 15756 15757 15758 15759 15760 15761 15762 15763 15764 15765 15766
        case VIR_DOMAIN_PAUSED_UNKNOWN:
            ;
        }
        break;

    case VIR_DOMAIN_SHUTDOWN:
        switch ((virDomainShutdownReason) reason) {
        case VIR_DOMAIN_SHUTDOWN_USER:
            return N_("user");
        case VIR_DOMAIN_SHUTDOWN_UNKNOWN:
            ;
        }
        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:
            ;
        }
        break;

    case VIR_DOMAIN_CRASHED:
        switch ((virDomainCrashedReason) reason) {
        case VIR_DOMAIN_CRASHED_UNKNOWN:
            ;
        }
        break;
15767 15768 15769

    default:
        ;
15770 15771 15772 15773 15774
    }

    return N_("unknown");
}

15775 15776 15777 15778 15779 15780 15781 15782 15783 15784 15785 15786 15787 15788 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 15799 15800 15801 15802 15803 15804 15805 15806 15807 15808 15809 15810 15811 15812 15813 15814 15815 15816 15817 15818 15819 15820 15821 15822 15823 15824 15825 15826 15827 15828 15829 15830 15831 15832 15833 15834 15835 15836 15837
static char *
vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item)
{
    int ret = 0;
    char *str = NULL;

    if (!ctl || !item)
        return 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;

    default:
        vshError(ctl, _("unimplemented block statistics parameter type"));
    }

    if (ret < 0)
        vshError(ctl, "%s", _("Out of memory"));
    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;
}

15838 15839 15840 15841 15842 15843 15844 15845 15846 15847 15848 15849 15850 15851 15852 15853 15854
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");
    }

    return N_("unknown");
}

15855 15856 15857 15858
static const char *
vshDomainVcpuStateToString(int state)
{
    switch (state) {
15859
    case VIR_VCPU_OFFLINE:
15860
        return N_("offline");
15861
    case VIR_VCPU_BLOCKED:
15862
        return N_("idle");
15863
    case VIR_VCPU_RUNNING:
15864
        return N_("running");
15865
    default:
15866
        ;/*FALLTHROUGH*/
15867
    }
15868
    return N_("no state");
15869 15870
}

E
Eric Blake 已提交
15871
static bool
15872
vshConnectionUsability(vshControl *ctl, virConnectPtr conn)
15873
{
15874 15875
    /* TODO: use something like virConnectionState() to
     *       check usability of the connection
K
Karel Zak 已提交
15876 15877
     */
    if (!conn) {
15878
        vshError(ctl, "%s", _("no valid connection"));
E
Eric Blake 已提交
15879
        return false;
K
Karel Zak 已提交
15880
    }
E
Eric Blake 已提交
15881
    return true;
K
Karel Zak 已提交
15882 15883
}

K
Karel Zak 已提交
15884
static void
15885
vshDebug(vshControl *ctl, int level, const char *format, ...)
15886
{
K
Karel Zak 已提交
15887
    va_list ap;
15888
    char *str;
K
Karel Zak 已提交
15889

15890 15891 15892 15893 15894 15895 15896
    /* Aligning log levels to that of libvirt.
     * Traces with levels >=  user-specified-level
     * gets logged into file
     */
    if (level < ctl->debug)
        return;

15897
    va_start(ap, format);
15898
    vshOutputLogFile(ctl, level, format, ap);
15899 15900
    va_end(ap);

K
Karel Zak 已提交
15901
    va_start(ap, format);
15902 15903 15904 15905 15906
    if (virVasprintf(&str, format, ap) < 0) {
        /* Skip debug messages on low memory */
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
15907
    va_end(ap);
15908 15909
    fputs(str, stdout);
    VIR_FREE(str);
K
Karel Zak 已提交
15910 15911 15912
}

static void
15913
vshPrintExtra(vshControl *ctl, const char *format, ...)
15914
{
K
Karel Zak 已提交
15915
    va_list ap;
15916
    char *str;
15917

15918
    if (ctl && ctl->quiet)
K
Karel Zak 已提交
15919
        return;
15920

K
Karel Zak 已提交
15921
    va_start(ap, format);
15922 15923 15924 15925 15926
    if (virVasprintf(&str, format, ap) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
        va_end(ap);
        return;
    }
K
Karel Zak 已提交
15927
    va_end(ap);
15928
    fputs(str, stdout);
15929
    VIR_FREE(str);
K
Karel Zak 已提交
15930 15931
}

K
Karel Zak 已提交
15932

K
Karel Zak 已提交
15933
static void
15934
vshError(vshControl *ctl, const char *format, ...)
15935
{
K
Karel Zak 已提交
15936
    va_list ap;
15937
    char *str;
15938

15939 15940 15941 15942 15943
    if (ctl != NULL) {
        va_start(ap, format);
        vshOutputLogFile(ctl, VSH_ERR_ERROR, format, ap);
        va_end(ap);
    }
15944

15945 15946 15947 15948
    /* 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);
15949
    fputs(_("error: "), stderr);
15950

K
Karel Zak 已提交
15951
    va_start(ap, format);
15952 15953 15954
    /* We can't recursively call vshError on an OOM situation, so ignore
       failure here. */
    ignore_value(virVasprintf(&str, format, ap));
K
Karel Zak 已提交
15955 15956
    va_end(ap);

15957
    fprintf(stderr, "%s\n", NULLSTR(str));
15958
    fflush(stderr);
15959
    VIR_FREE(str);
K
Karel Zak 已提交
15960 15961
}

15962

K
Karel Zak 已提交
15963
/*
15964
 * Initialize connection.
K
Karel Zak 已提交
15965
 */
E
Eric Blake 已提交
15966
static bool
15967
vshInit(vshControl *ctl)
15968
{
15969 15970
    char *debugEnv;

K
Karel Zak 已提交
15971
    if (ctl->conn)
E
Eric Blake 已提交
15972
        return false;
K
Karel Zak 已提交
15973

J
Jiri Denemark 已提交
15974
    if (ctl->debug == VSH_DEBUG_DEFAULT) {
15975 15976 15977
        /* log level not set from commandline, check env variable */
        debugEnv = getenv("VIRSH_DEBUG");
        if (debugEnv) {
J
Jiri Denemark 已提交
15978 15979 15980
            int debug;
            if (virStrToLong_i(debugEnv, NULL, 10, &debug) < 0 ||
                debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) {
15981 15982
                vshError(ctl, "%s",
                         _("VIRSH_DEBUG not set with a valid numeric value"));
J
Jiri Denemark 已提交
15983 15984
            } else {
                ctl->debug = debug;
15985 15986 15987 15988 15989 15990 15991 15992 15993 15994 15995 15996
            }
        }
    }

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

15997 15998
    vshOpenLogFile(ctl);

15999 16000
    /* set up the library error handler */
    virSetErrorFunc(NULL, virshErrorHandler);
16001

16002 16003 16004
    /* set up the signals handlers to catch disconnections */
    vshSetupSignals();

16005
    if (virEventRegisterDefaultImpl() < 0)
E
Eric Blake 已提交
16006
        return false;
16007

16008 16009 16010 16011
    if (ctl->name) {
        ctl->conn = virConnectOpenAuth(ctl->name,
                                       virConnectAuthPtrDefault,
                                       ctl->readonly ? VIR_CONNECT_RO : 0);
16012

16013 16014 16015 16016 16017 16018 16019 16020 16021 16022 16023
        /* 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;
        }
16024
    }
K
Karel Zak 已提交
16025

E
Eric Blake 已提交
16026
    return true;
K
Karel Zak 已提交
16027 16028
}

16029 16030
#define LOGFILE_FLAGS (O_WRONLY | O_APPEND | O_CREAT | O_SYNC)

16031 16032 16033 16034 16035 16036 16037 16038 16039 16040 16041 16042 16043 16044 16045 16046 16047 16048 16049
/**
 * 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:
16050
                vshError(ctl, "%s",
J
Jim Meyering 已提交
16051
                         _("failed to get the log file information"));
16052
                exit(EXIT_FAILURE);
16053 16054 16055
        }
    } else {
        if (!S_ISREG(st.st_mode)) {
16056 16057
            vshError(ctl, "%s", _("the log path is not a file"));
            exit(EXIT_FAILURE);
16058 16059 16060 16061
        }
    }

    /* log file open */
16062
    if ((ctl->log_fd = open(ctl->logfile, LOGFILE_FLAGS, FILE_MODE)) < 0) {
16063
        vshError(ctl, "%s",
J
Jim Meyering 已提交
16064
                 _("failed to open the log file. check the log file path"));
16065
        exit(EXIT_FAILURE);
16066 16067 16068 16069 16070 16071 16072 16073 16074
    }
}

/**
 * vshOutputLogFile:
 *
 * Outputting an error to log file.
 */
static void
16075 16076
vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format,
                 va_list ap)
16077
{
16078 16079 16080
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *str;
    size_t len;
16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094
    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);
16095
    virBufferAsprintf(&buf, "[%d.%02d.%02d %02d:%02d:%02d %s %d] ",
16096 16097 16098 16099 16100 16101
                      (1900 + stTm->tm_year),
                      (1 + stTm->tm_mon),
                      stTm->tm_mday,
                      stTm->tm_hour,
                      stTm->tm_min,
                      stTm->tm_sec,
16102 16103
                      SIGN_NAME,
                      (int) getpid());
16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 16117 16118 16119 16120 16121 16122 16123
    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;
    }
16124 16125 16126
    virBufferAsprintf(&buf, "%s ", lvl);
    virBufferVasprintf(&buf, msg_format, ap);
    virBufferAddChar(&buf, '\n');
16127

16128 16129
    if (virBufferError(&buf))
        goto error;
16130

16131 16132 16133 16134 16135
    str = virBufferContentAndReset(&buf);
    len = strlen(str);
    if (len > 1 && str[len - 2] == '\n') {
        str[len - 1] = '\0';
        len--;
16136
    }
16137

16138 16139 16140 16141 16142 16143 16144 16145 16146 16147 16148
    /* 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);
16149 16150 16151 16152 16153 16154 16155 16156 16157 16158 16159
}

/**
 * vshCloseLogFile:
 *
 * Close log file.
 */
static void
vshCloseLogFile(vshControl *ctl)
{
    /* log file close */
16160 16161 16162
    if (VIR_CLOSE(ctl->log_fd) < 0) {
        vshError(ctl, _("%s: failed to write log file: %s"),
                 ctl->logfile ? ctl->logfile : "?", strerror (errno));
16163 16164 16165
    }

    if (ctl->logfile) {
16166
        VIR_FREE(ctl->logfile);
16167 16168 16169 16170
        ctl->logfile = NULL;
    }
}

16171
#ifdef USE_READLINE
16172

K
Karel Zak 已提交
16173 16174 16175 16176 16177
/* -----------------
 * Readline stuff
 * -----------------
 */

16178
/*
K
Karel Zak 已提交
16179 16180
 * Generator function for command completion.  STATE lets us
 * know whether to start from scratch; without any state
16181
 * (i.e. STATE == 0), then we start at the top of the list.
K
Karel Zak 已提交
16182 16183
 */
static char *
16184 16185
vshReadlineCommandGenerator(const char *text, int state)
{
16186
    static int grp_list_index, cmd_list_index, len;
K
Karel Zak 已提交
16187
    const char *name;
16188 16189
    const vshCmdGrp *grp;
    const vshCmdDef *cmds;
K
Karel Zak 已提交
16190 16191

    if (!state) {
16192 16193
        grp_list_index = 0;
        cmd_list_index = 0;
16194
        len = strlen(text);
K
Karel Zak 已提交
16195 16196
    }

16197 16198
    grp = cmdGroups;

K
Karel Zak 已提交
16199
    /* Return the next name which partially matches from the
16200
     * command list.
K
Karel Zak 已提交
16201
     */
16202 16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 16213 16214 16215
    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 已提交
16216 16217 16218 16219 16220 16221 16222
    }

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

static char *
16223 16224
vshReadlineOptionsGenerator(const char *text, int state)
{
K
Karel Zak 已提交
16225
    static int list_index, len;
16226
    static const vshCmdDef *cmd = NULL;
K
Karel Zak 已提交
16227
    const char *name;
K
Karel Zak 已提交
16228 16229 16230 16231 16232 16233 16234 16235 16236

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

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

16237
        cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1);
16238
        memcpy(cmdname, rl_line_buffer, p - rl_line_buffer);
K
Karel Zak 已提交
16239 16240 16241

        cmd = vshCmddefSearch(cmdname);
        list_index = 0;
16242
        len = strlen(text);
16243
        VIR_FREE(cmdname);
K
Karel Zak 已提交
16244 16245 16246 16247
    }

    if (!cmd)
        return NULL;
16248

16249 16250 16251
    if (!cmd->opts)
        return NULL;

K
Karel Zak 已提交
16252
    while ((name = cmd->opts[list_index].name)) {
16253
        const vshCmdOptDef *opt = &cmd->opts[list_index];
K
Karel Zak 已提交
16254
        char *res;
16255

K
Karel Zak 已提交
16256
        list_index++;
16257

16258
        if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV)
K
Karel Zak 已提交
16259 16260
            /* ignore non --option */
            continue;
16261

K
Karel Zak 已提交
16262
        if (len > 2) {
16263
            if (STRNEQLEN(name, text + 2, len - 2))
K
Karel Zak 已提交
16264 16265
                continue;
        }
16266
        res = vshMalloc(NULL, strlen(name) + 3);
16267
        snprintf(res, strlen(name) + 3,  "--%s", name);
K
Karel Zak 已提交
16268 16269 16270 16271 16272 16273 16274 16275
        return res;
    }

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

static char **
16276 16277 16278
vshReadlineCompletion(const char *text, int start,
                      int end ATTRIBUTE_UNUSED)
{
K
Karel Zak 已提交
16279 16280
    char **matches = (char **) NULL;

16281
    if (start == 0)
K
Karel Zak 已提交
16282
        /* command name generator */
16283
        matches = rl_completion_matches(text, vshReadlineCommandGenerator);
K
Karel Zak 已提交
16284 16285
    else
        /* commands options */
16286
        matches = rl_completion_matches(text, vshReadlineOptionsGenerator);
K
Karel Zak 已提交
16287 16288 16289 16290
    return matches;
}


16291 16292
static int
vshReadlineInit(vshControl *ctl)
16293
{
16294 16295
    char *userdir = NULL;

K
Karel Zak 已提交
16296 16297 16298 16299 16300
    /* 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;
16301 16302 16303

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

    /* Prepare to read/write history from/to the ~/.virsh/history file */
16306
    userdir = virGetUserDirectory(getuid());
16307

16308 16309
    if (userdir == NULL) {
        vshError(ctl, "%s", _("Could not determine home directory"));
16310
        return -1;
16311
    }
16312 16313 16314

    if (virAsprintf(&ctl->historydir, "%s/.virsh", userdir) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
16315
        VIR_FREE(userdir);
16316 16317 16318 16319 16320
        return -1;
    }

    if (virAsprintf(&ctl->historyfile, "%s/history", ctl->historydir) < 0) {
        vshError(ctl, "%s", _("Out of memory"));
16321
        VIR_FREE(userdir);
16322 16323 16324
        return -1;
    }

16325
    VIR_FREE(userdir);
16326 16327 16328 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343

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

16344 16345
    VIR_FREE(ctl->historydir);
    VIR_FREE(ctl->historyfile);
K
Karel Zak 已提交
16346 16347
}

16348 16349 16350 16351 16352 16353
static char *
vshReadline (vshControl *ctl ATTRIBUTE_UNUSED, const char *prompt)
{
    return readline (prompt);
}

16354
#else /* !USE_READLINE */
16355

16356 16357 16358 16359 16360 16361 16362
static int
vshReadlineInit (vshControl *ctl ATTRIBUTE_UNUSED)
{
    /* empty */
    return 0;
}

16363
static void
16364
vshReadlineDeinit (vshControl *ctl ATTRIBUTE_UNUSED)
16365 16366 16367 16368 16369 16370 16371 16372 16373 16374 16375 16376 16377 16378 16379 16380 16381 16382 16383 16384 16385 16386 16387
{
    /* 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);
}

16388
#endif /* !USE_READLINE */
16389

K
Karel Zak 已提交
16390
/*
J
Jim Meyering 已提交
16391
 * Deinitialize virsh
K
Karel Zak 已提交
16392
 */
E
Eric Blake 已提交
16393
static bool
16394
vshDeinit(vshControl *ctl)
16395
{
16396
    vshReadlineDeinit(ctl);
16397
    vshCloseLogFile(ctl);
16398
    VIR_FREE(ctl->name);
K
Karel Zak 已提交
16399
    if (ctl->conn) {
16400 16401 16402
        int ret;
        if ((ret = virConnectClose(ctl->conn)) != 0) {
            vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
K
Karel Zak 已提交
16403 16404
        }
    }
D
Daniel P. Berrange 已提交
16405 16406
    virResetLastError();

E
Eric Blake 已提交
16407
    return true;
K
Karel Zak 已提交
16408
}
16409

K
Karel Zak 已提交
16410 16411 16412 16413
/*
 * Print usage
 */
static void
16414
vshUsage(void)
16415
{
16416
    const vshCmdGrp *grp;
16417
    const vshCmdDef *cmd;
16418

L
Lai Jiangshan 已提交
16419 16420
    fprintf(stdout, _("\n%s [options]... [<command_string>]"
                      "\n%s [options]... <command> [args...]\n\n"
16421 16422 16423
                      "  options:\n"
                      "    -c | --connect <uri>    hypervisor connection URI\n"
                      "    -r | --readonly         connect readonly\n"
16424
                      "    -d | --debug <num>      debug level [0-4]\n"
16425 16426 16427 16428
                      "    -h | --help             this help\n"
                      "    -q | --quiet            quiet mode\n"
                      "    -t | --timing           print timing information\n"
                      "    -l | --log <file>       output logging to file\n"
16429
                      "    -v | --version[=short]  program version\n"
E
Eric Blake 已提交
16430
                      "    -V | --version=long     version and full options\n\n"
16431
                      "  commands (non interactive mode):\n\n"), progname, progname);
16432

16433 16434
    for (grp = cmdGroups; grp->name; grp++) {
        fprintf(stdout, _(" %s (help keyword '%s')\n"), grp->name, grp->keyword);
16435

16436 16437 16438 16439 16440 16441 16442 16443 16444
        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"));
16445 16446 16447
    fprintf(stdout, "%s",
            _("\n  (specify help <command> for details about the command)\n\n"));
    return;
K
Karel Zak 已提交
16448 16449
}

16450 16451 16452 16453 16454 16455 16456 16457 16458 16459
/*
 * 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 已提交
16460 16461
    vshPrint(ctl, "%s", _("Compiled with support for:\n"));
    vshPrint(ctl, "%s", _(" Hypervisors:"));
16462 16463 16464 16465 16466 16467 16468 16469 16470 16471 16472 16473 16474 16475 16476 16477 16478 16479 16480 16481 16482 16483 16484 16485 16486 16487 16488 16489 16490 16491 16492 16493 16494 16495 16496
#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 已提交
16497
    vshPrint(ctl, "%s", _(" Networking:"));
16498 16499 16500 16501 16502 16503 16504 16505 16506 16507 16508 16509 16510 16511 16512 16513 16514 16515 16516 16517 16518 16519 16520 16521 16522 16523
#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 已提交
16524
    vshPrint(ctl, "%s", _(" Storage:"));
16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547
#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 已提交
16548
    vshPrint(ctl, "%s", _(" Miscellaneous:"));
16549
#ifdef WITH_SECDRIVER_APPARMOR
16550 16551 16552 16553 16554 16555 16556 16557 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572
    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 已提交
16573 16574 16575 16576
/*
 * argv[]:  virsh [options] [command]
 *
 */
E
Eric Blake 已提交
16577
static bool
16578
vshParseArgv(vshControl *ctl, int argc, char **argv)
16579
{
16580 16581
    bool help = false;
    int arg;
K
Karel Zak 已提交
16582
    struct option opt[] = {
E
Eric Blake 已提交
16583 16584 16585 16586 16587 16588 16589 16590 16591
        {"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'},
        {NULL, 0, NULL, 0}
16592 16593
    };

16594 16595 16596
    /* 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. */
16597
    while ((arg = getopt_long(argc, argv, "+d:hqtc:vVrl:", opt, NULL)) != -1) {
16598
        switch (arg) {
16599
        case 'd':
D
Daniel Veillard 已提交
16600
            if (virStrToLong_i(optarg, NULL, 10, &ctl->debug) < 0) {
L
Laine Stump 已提交
16601
                vshError(ctl, "%s", _("option -d takes a numeric argument"));
D
Daniel Veillard 已提交
16602 16603
                exit(EXIT_FAILURE);
            }
16604 16605
            break;
        case 'h':
16606
            help = true;
16607 16608
            break;
        case 'q':
E
Eric Blake 已提交
16609
            ctl->quiet = true;
16610 16611
            break;
        case 't':
E
Eric Blake 已提交
16612
            ctl->timing = true;
16613 16614 16615 16616
            break;
        case 'c':
            ctl->name = vshStrdup(ctl, optarg);
            break;
E
Eric Blake 已提交
16617 16618 16619 16620 16621 16622
        case 'v':
            if (STRNEQ_NULLABLE(optarg, "long")) {
                puts(VERSION);
                exit(EXIT_SUCCESS);
            }
            /* fall through */
16623 16624 16625
        case 'V':
            vshShowVersion(ctl);
            exit(EXIT_SUCCESS);
16626
        case 'r':
E
Eric Blake 已提交
16627
            ctl->readonly = true;
16628
            break;
16629 16630 16631
        case 'l':
            ctl->logfile = vshStrdup(ctl, optarg);
            break;
16632
        default:
16633 16634
            vshError(ctl, _("unsupported option '-%c'. See --help."), arg);
            exit(EXIT_FAILURE);
K
Karel Zak 已提交
16635 16636 16637 16638
        }
    }

    if (help) {
16639 16640
        if (optind < argc) {
            vshError(ctl, _("extra argument '%s'. See --help."), argv[optind]);
16641 16642
            exit(EXIT_FAILURE);
        }
16643 16644 16645

        /* list all command */
        vshUsage();
K
Karel Zak 已提交
16646
        exit(EXIT_SUCCESS);
16647 16648
    }

16649
    if (argc > optind) {
K
Karel Zak 已提交
16650
        /* parse command */
E
Eric Blake 已提交
16651
        ctl->imode = false;
16652
        if (argc - optind == 1) {
16653
            vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]);
16654
            return vshCommandStringParse(ctl, argv[optind]);
L
Lai Jiangshan 已提交
16655
        } else {
16656
            return vshCommandArgvParse(ctl, argc - optind, argv + optind);
K
Karel Zak 已提交
16657 16658
        }
    }
E
Eric Blake 已提交
16659
    return true;
K
Karel Zak 已提交
16660 16661
}

16662 16663 16664 16665
int
main(int argc, char **argv)
{
    vshControl _ctl, *ctl = &_ctl;
16666
    char *defaultConn;
E
Eric Blake 已提交
16667
    bool ret = true;
K
Karel Zak 已提交
16668

16669 16670 16671
    memset(ctl, 0, sizeof(vshControl));
    ctl->imode = true;          /* default is interactive mode */
    ctl->log_fd = -1;           /* Initialize log file descriptor */
J
Jiri Denemark 已提交
16672
    ctl->debug = VSH_DEBUG_DEFAULT;
16673

16674 16675
    if (!setlocale(LC_ALL, "")) {
        perror("setlocale");
16676
        /* failure to setup locale is not fatal */
16677
    }
16678
    if (!bindtextdomain(PACKAGE, LOCALEDIR)) {
16679
        perror("bindtextdomain");
E
Eric Blake 已提交
16680
        return EXIT_FAILURE;
16681
    }
16682
    if (!textdomain(PACKAGE)) {
16683
        perror("textdomain");
E
Eric Blake 已提交
16684
        return EXIT_FAILURE;
16685 16686
    }

16687 16688 16689 16690 16691
    if (virInitialize() < 0) {
        vshError(ctl, "%s", _("Failed to initialize libvirt"));
        return EXIT_FAILURE;
    }

16692
    if (!(progname = strrchr(argv[0], '/')))
K
Karel Zak 已提交
16693 16694 16695
        progname = argv[0];
    else
        progname++;
16696

16697
    if ((defaultConn = getenv("VIRSH_DEFAULT_CONNECT_URI"))) {
E
Eric Blake 已提交
16698
        ctl->name = vshStrdup(ctl, defaultConn);
16699 16700
    }

D
Daniel P. Berrange 已提交
16701 16702
    if (!vshParseArgv(ctl, argc, argv)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
16703
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
16704
    }
16705

D
Daniel P. Berrange 已提交
16706 16707
    if (!vshInit(ctl)) {
        vshDeinit(ctl);
K
Karel Zak 已提交
16708
        exit(EXIT_FAILURE);
D
Daniel P. Berrange 已提交
16709
    }
16710

K
Karel Zak 已提交
16711
    if (!ctl->imode) {
16712
        ret = vshCommandRun(ctl, ctl->cmd);
16713
    } else {
K
Karel Zak 已提交
16714 16715
        /* interactive mode */
        if (!ctl->quiet) {
K
Karel Zak 已提交
16716
            vshPrint(ctl,
16717
                     _("Welcome to %s, the virtualization interactive terminal.\n\n"),
16718
                     progname);
J
Jim Meyering 已提交
16719
            vshPrint(ctl, "%s",
16720
                     _("Type:  'help' for help with commands\n"
16721
                       "       'quit' to quit\n\n"));
K
Karel Zak 已提交
16722
        }
16723 16724 16725 16726 16727 16728

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

K
Karel Zak 已提交
16729
        do {
16730
            const char *prompt = ctl->readonly ? VSH_PROMPT_RO : VSH_PROMPT_RW;
16731
            ctl->cmdstr =
16732
                vshReadline(ctl, prompt);
16733 16734
            if (ctl->cmdstr == NULL)
                break;          /* EOF */
K
Karel Zak 已提交
16735
            if (*ctl->cmdstr) {
16736
#if USE_READLINE
K
Karel Zak 已提交
16737
                add_history(ctl->cmdstr);
16738
#endif
16739
                if (vshCommandStringParse(ctl, ctl->cmdstr))
K
Karel Zak 已提交
16740 16741
                    vshCommandRun(ctl, ctl->cmd);
            }
16742
            VIR_FREE(ctl->cmdstr);
16743
        } while (ctl->imode);
K
Karel Zak 已提交
16744

16745 16746
        if (ctl->cmdstr == NULL)
            fputc('\n', stdout);        /* line break after alone prompt */
K
Karel Zak 已提交
16747
    }
16748

K
Karel Zak 已提交
16749 16750
    vshDeinit(ctl);
    exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
16751
}