diff --git a/.x-sc_prohibit_asprintf b/.x-sc_prohibit_asprintf index 614c238..d03b947 100644 --- a/.x-sc_prohibit_asprintf +++ b/.x-sc_prohibit_asprintf @@ -1,3 +1,5 @@ +ChangeLog +^bootstrap.conf$ ^gnulib/ ^po/ -ChangeLog +^src/util/util.c$ diff --git a/cfg.mk b/cfg.mk index 2ac3c69..6312632 100644 --- a/cfg.mk +++ b/cfg.mk @@ -243,7 +243,7 @@ sc_prohibit_strncmp: # Use virAsprintf rather than as'printf since *strp is undefined on error. sc_prohibit_asprintf: - @prohibit='\' \ + @prohibit='\' \ halt='use virAsprintf, not as'printf \ $(_sc_search_regexp) diff --git a/docs/internals/command.html.in b/docs/internals/command.html.in index 3d28cd4..c4fa800 100644 --- a/docs/internals/command.html.in +++ b/docs/internals/command.html.in @@ -111,6 +111,15 @@

+ If an argument needs to be formatted as if by + printf: +

+ +
+  virCommandAddArgFormat(cmd, "%d", count);
+
+ +

To add a entire NULL terminated array of arguments in one go, there are two options.

@@ -124,8 +133,8 @@

- This latter option can also be used at time of initial - construction of the virCommandPtr object + This can also be done at the time of initial construction of + the virCommandPtr object:

@@ -134,7 +143,9 @@
       "--strict-order", "--except-interface",
       "lo", "--domain", "localdomain", NULL
   };
-  virCommandPtr cmd = virCommandNewArgs(cmd, args);
+  virCommandPtr cmd1 = virCommandNewArgs(cmd, args);
+  virCommandPtr cmd2 = virCommandNewArgList("/usr/bin/dnsmasq",
+                                            "--domain", "localdomain", NULL);
 

Setting up the environment

@@ -233,23 +244,31 @@

Managing file handles

- To prevent unintended resource leaks to child processes, - all open file handles will be closed in the child, and - its stdin/out/err all connected to /dev/null. - It is possible to allow an open file handle to be passed - into the child: + To prevent unintended resource leaks to child processes, the + child defaults to closing all open file handles, and setting + stdin/out/err to /dev/null. It is possible to + allow an open file handle to be passed into the child, while + controlling whether that handle remains open in the parent or + guaranteeing that the handle will be closed in the parent after + either virCommandRun or virCommandFree.

-  virCommandPreserveFD(cmd, 5);
+  int sharedfd = open("cmd.log", "w+");
+  int childfd = open("conf.txt", "r");
+  virCommandPreserveFD(cmd, sharedfd);
+  virCommandTransferFD(cmd, childfd);
+  if (VIR_CLOSE(sharedfd) < 0)
+      goto cleanup;
 

- With this file descriptor 5 in the current process remains - open as file descriptor 5 in the child. For stdin/out/err - it is usually necessary to map a file handle. To attach - file descriptor 7 in the current process to stdin in the - child: + With this, both file descriptors sharedfd and childfd in the + current process remain open as the same file descriptors in the + child. Meanwhile, after the child is spawned, sharedfd remains + open in the parent, while childfd is closed. For stdin/out/err + it is usually necessary to map a file handle. To attach file + descriptor 7 in the current process to stdin in the child:

@@ -284,16 +303,30 @@
     

Once the command is running, outfd and errfd will be initialized with - valid file handles that can be read from. + valid file handles that can be read from. It is + permissible to pass the same pointer for both outfd + and errfd, in which case both standard streams in + the child will share the same fd in the parent.

+

+ Normally, file descriptors opened to collect output from a child + process perform blocking I/O, but the parent process can request + non-blocking mode: +

+ +
+  virCommandNonblockingFDs(cmd);
+
+

Feeding & capturing strings to/from the child

Often dealing with file handles for stdin/out/err is unnecessarily complex. It is possible to specify a string buffer to act as the data source for the - child's stdin, if there are no embedded NUL bytes + child's stdin, if there are no embedded NUL bytes, + and if the command will be run with virCommandRun:

@@ -304,7 +337,8 @@
     

Similarly it is possible to request that the child's stdout/err be redirected into a string buffer, if the - output is not expected to contain NUL bytes + output is not expected to contain NUL bytes, and if + the command will be run with virCommandRun:

@@ -346,6 +380,31 @@
   virCommandSetPreExecHook(cmd, hook, opaque);
 
+

Logging commands

+ +

+ Sometimes, it is desirable to log what command will be run, or + even to use virCommand solely for creation of a single + consolidated string without running anything. +

+ +
+  int logfd = ...;
+  char *timestamp = virTimestamp();
+  char *string = NULL;
+
+  dprintf(logfd, "%s: ", timestamp);
+  VIR_FREE(timestamp);
+  virCommandWriteArgLog(cmd, logfd);
+
+  string = virCommandToString(cmd);
+  if (string)
+      VIR_DEBUG("about to run %s", string);
+  VIR_FREE(string);
+  if (virCommandRun(cmd) < 0)
+      return -1;
+
+

Running commands synchronously

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 63795b3..d7dc7b1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -85,6 +85,7 @@ virCgroupSetSwapHardLimit; # command.h virCommandAddArg; +virCommandAddArgFormat; virCommandAddArgList; virCommandAddArgPair; virCommandAddArgSet; @@ -96,7 +97,9 @@ virCommandClearCaps; virCommandDaemonize; virCommandFree; virCommandNew; +virCommandNewArgList; virCommandNewArgs; +virCommandNonblockingFDs; virCommandPreserveFD; virCommandRun; virCommandRunAsync; @@ -109,7 +112,10 @@ virCommandSetOutputFD; virCommandSetPidFile; virCommandSetPreExecHook; virCommandSetWorkingDirectory; +virCommandToString; +virCommandTransferFD; virCommandWait; +virCommandWriteArgLog; # conf.h @@ -839,6 +845,7 @@ virStrToLong_ull; virStrcpy; virStrncpy; virTimestamp; +virVasprintf; # uuid.h diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 35caccc..7c0db15 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1530,6 +1530,15 @@ int qemudExtractVersionInfo(const char *qemu, if (retversion) *retversion = 0; + /* Make sure the binary we are about to try exec'ing exists. + * Technically we could catch the exec() failure, but that's + * in a sub-process so it's hard to feed back a useful error. + */ + if (access(qemu, X_OK) < 0) { + virReportSystemError(errno, _("Cannot find QEMU binary %s"), qemu); + return -1; + } + if (virExec(qemuarg, qemuenv, NULL, &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) return -1; @@ -3942,43 +3951,35 @@ qemuBuildSmpArgStr(const virDomainDefPtr def, * XXX 'conn' is only required to resolve network -> bridge name * figure out how to remove this requirement some day */ -int qemudBuildCommandLine(virConnectPtr conn, - struct qemud_driver *driver, - virDomainDefPtr def, - virDomainChrDefPtr monitor_chr, - int monitor_json, - unsigned long long qemuCmdFlags, - const char ***retargv, - const char ***retenv, - int **vmfds, - int *nvmfds, - const char *migrateFrom, - virDomainSnapshotObjPtr current_snapshot) +virCommandPtr +qemudBuildCommandLine(virConnectPtr conn, + struct qemud_driver *driver, + virDomainDefPtr def, + virDomainChrDefPtr monitor_chr, + bool monitor_json, + unsigned long long qemuCmdFlags, + const char *migrateFrom, + virDomainSnapshotObjPtr current_snapshot) { int i; - char memory[50]; char boot[VIR_DOMAIN_BOOT_LAST+1]; struct utsname ut; int disableKQEMU = 0; int enableKQEMU = 0; int disableKVM = 0; int enableKVM = 0; - int qargc = 0, qarga = 0; - const char **qargv = NULL; - int qenvc = 0, qenva = 0; - const char **qenv = NULL; const char *emulator; char uuid[VIR_UUID_STRING_BUFLEN]; - char domid[50]; char *cpu; char *smp; int last_good_net = -1; bool hasHwVirt = false; + virCommandPtr cmd; uname_normalize(&ut); if (qemuAssignDeviceAliases(def, qemuCmdFlags) < 0) - return -1; + return NULL; virUUIDFormat(def->uuid, uuid); @@ -3990,7 +3991,7 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("TCP migration is not supported with this QEMU binary")); - return -1; + return NULL; } } else if (STREQ(migrateFrom, "stdio")) { if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { @@ -3998,13 +3999,13 @@ int qemudBuildCommandLine(virConnectPtr conn, } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("STDIO migration is not supported with this QEMU binary")); - return -1; + return NULL; } } else if (STRPREFIX(migrateFrom, "exec")) { if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("STDIO migration is not supported with this QEMU binary")); - return -1; + return NULL; } } } @@ -4065,134 +4066,45 @@ int qemudBuildCommandLine(virConnectPtr conn, break; } -#define ADD_ARG_SPACE \ - do { \ - if (qargc == qarga) { \ - qarga += 10; \ - if (VIR_REALLOC_N(qargv, qarga) < 0) \ - goto no_memory; \ - } \ - } while (0) - -#define ADD_ARG(thisarg) \ - do { \ - ADD_ARG_SPACE; \ - qargv[qargc++] = thisarg; \ - } while (0) - -#define ADD_ARG_LIT(thisarg) \ - do { \ - ADD_ARG_SPACE; \ - if ((qargv[qargc++] = strdup(thisarg)) == NULL) \ - goto no_memory; \ - } while (0) - -#define ADD_USBDISK(thisarg) \ - do { \ - ADD_ARG_LIT("-usbdevice"); \ - ADD_ARG_SPACE; \ - if ((virAsprintf((char **)&(qargv[qargc++]), \ - "disk:%s", thisarg)) == -1) { \ - goto no_memory; \ - } \ - } while (0) - -#define ADD_ENV_SPACE \ - do { \ - if (qenvc == qenva) { \ - qenva += 10; \ - if (VIR_REALLOC_N(qenv, qenva) < 0) \ - goto no_memory; \ - } \ - } while (0) - -#define ADD_ENV(thisarg) \ - do { \ - ADD_ENV_SPACE; \ - qenv[qenvc++] = thisarg; \ - } while (0) - -#define ADD_ENV_LIT(thisarg) \ - do { \ - ADD_ENV_SPACE; \ - if ((qenv[qenvc++] = strdup(thisarg)) == NULL) \ - goto no_memory; \ - } while (0) - -#define ADD_ENV_PAIR(envname, val) \ - do { \ - char *envval; \ - ADD_ENV_SPACE; \ - if (virAsprintf(&envval, "%s=%s", envname, val) < 0) \ - goto no_memory; \ - qenv[qenvc++] = envval; \ - } while (0) - - /* Make sure to unset or set all envvars in qemuxml2argvtest.c that - * are copied here using this macro, otherwise the test may fail */ -#define ADD_ENV_COPY(envname) \ - do { \ - char *val = getenv(envname); \ - if (val != NULL) { \ - ADD_ENV_PAIR(envname, val); \ - } \ - } while (0) + cmd = virCommandNewArgList(emulator, "-S", NULL); - /* Set '-m MB' based on maxmem, because the lower 'memory' limit - * is set post-startup using the balloon driver. If balloon driver - * is not supported, then they're out of luck anyway - */ - snprintf(memory, sizeof(memory), "%lu", def->mem.max_balloon/1024); - snprintf(domid, sizeof(domid), "%d", def->id); - - ADD_ENV_LIT("LC_ALL=C"); - - ADD_ENV_COPY("LD_PRELOAD"); - ADD_ENV_COPY("LD_LIBRARY_PATH"); - ADD_ENV_COPY("PATH"); - ADD_ENV_COPY("HOME"); - ADD_ENV_COPY("USER"); - ADD_ENV_COPY("LOGNAME"); - ADD_ENV_COPY("TMPDIR"); - - ADD_ARG_LIT(emulator); - ADD_ARG_LIT("-S"); + virCommandAddEnvPassCommon(cmd); /* This should *never* be NULL, since we always provide * a machine in the capabilities data for QEMU. So this * check is just here as a safety in case the unexpected * happens */ - if (def->os.machine) { - ADD_ARG_LIT("-M"); - ADD_ARG_LIT(def->os.machine); - } + if (def->os.machine) + virCommandAddArgList(cmd, "-M", def->os.machine, NULL); if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, &ut, &cpu, &hasHwVirt) < 0) goto error; if (cpu) { - ADD_ARG_LIT("-cpu"); - ADD_ARG_LIT(cpu); + virCommandAddArgList(cmd, "-cpu", cpu, NULL); VIR_FREE(cpu); if ((qemuCmdFlags & QEMUD_CMD_FLAG_NESTING) && hasHwVirt) - ADD_ARG_LIT("-enable-nesting"); + virCommandAddArg(cmd, "-enable-nesting"); } if (disableKQEMU) - ADD_ARG_LIT("-no-kqemu"); - else if (enableKQEMU) { - ADD_ARG_LIT("-enable-kqemu"); - ADD_ARG_LIT("-kernel-kqemu"); - } + virCommandAddArg(cmd, "-no-kqemu"); + else if (enableKQEMU) + virCommandAddArgList(cmd, "-enable-kqemu", "-kernel-kqemu", NULL); if (disableKVM) - ADD_ARG_LIT("-no-kvm"); + virCommandAddArg(cmd, "-no-kvm"); if (enableKVM) - ADD_ARG_LIT("-enable-kvm"); - ADD_ARG_LIT("-m"); - ADD_ARG_LIT(memory); + virCommandAddArg(cmd, "-enable-kvm"); + + /* Set '-m MB' based on maxmem, because the lower 'memory' limit + * is set post-startup using the balloon driver. If balloon driver + * is not supported, then they're out of luck anyway + */ + virCommandAddArg(cmd, "-m"); + virCommandAddArgFormat(cmd, "%lu", def->mem.max_balloon / 1024); if (def->mem.hugepage_backed) { if (!driver->hugetlbfs_mount) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -4210,43 +4122,38 @@ int qemudBuildCommandLine(virConnectPtr conn, def->emulator); goto error; } - ADD_ARG_LIT("-mem-prealloc"); - ADD_ARG_LIT("-mem-path"); - ADD_ARG_LIT(driver->hugepage_path); + virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", + driver->hugepage_path, NULL); } - ADD_ARG_LIT("-smp"); + virCommandAddArg(cmd, "-smp"); if (!(smp = qemuBuildSmpArgStr(def, qemuCmdFlags))) goto error; - ADD_ARG(smp); + virCommandAddArg(cmd, smp); + VIR_FREE(smp); if (qemuCmdFlags & QEMUD_CMD_FLAG_NAME) { - ADD_ARG_LIT("-name"); + virCommandAddArg(cmd, "-name"); if (driver->setProcessName && (qemuCmdFlags & QEMUD_CMD_FLAG_NAME_PROCESS)) { - char *name; - if (virAsprintf(&name, "%s,process=qemu:%s", - def->name, def->name) < 0) - goto no_memory; - ADD_ARG_LIT(name); + virCommandAddArgFormat(cmd, "%s,process=qemu:%s", + def->name, def->name); } else { - ADD_ARG_LIT(def->name); + virCommandAddArg(cmd, def->name); } } - if (qemuCmdFlags & QEMUD_CMD_FLAG_UUID) { - ADD_ARG_LIT("-uuid"); - ADD_ARG_LIT(uuid); - } + if (qemuCmdFlags & QEMUD_CMD_FLAG_UUID) + virCommandAddArgList(cmd, "-uuid", uuid, NULL); if (def->virtType == VIR_DOMAIN_VIRT_XEN || STREQ(def->os.type, "xen") || STREQ(def->os.type, "linux")) { if (qemuCmdFlags & QEMUD_CMD_FLAG_DOMID) { - ADD_ARG_LIT("-domid"); - ADD_ARG_LIT(domid); + virCommandAddArg(cmd, "-domid"); + virCommandAddArgFormat(cmd, "%d", def->id); } else if (qemuCmdFlags & QEMUD_CMD_FLAG_XEN_DOMID) { - ADD_ARG_LIT("-xen-attach"); - ADD_ARG_LIT("-xen-domid"); - ADD_ARG_LIT(domid); + virCommandAddArg(cmd, "-xen-attach"); + virCommandAddArg(cmd, "-xen-domid"); + virCommandAddArgFormat(cmd, "%d", def->id); } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("qemu emulator '%s' does not support xen"), @@ -4288,13 +4195,13 @@ int qemudBuildCommandLine(virConnectPtr conn, smbioscmd = qemuBuildSmbiosBiosStr(source); if (smbioscmd != NULL) { - ADD_ARG_LIT("-smbios"); - ADD_ARG(smbioscmd); + virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); + VIR_FREE(smbioscmd); } smbioscmd = qemuBuildSmbiosSystemStr(source); if (smbioscmd != NULL) { - ADD_ARG_LIT("-smbios"); - ADD_ARG(smbioscmd); + virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); + VIR_FREE(smbioscmd); } } } @@ -4307,12 +4214,14 @@ int qemudBuildCommandLine(virConnectPtr conn, * these defaults ourselves... */ if (!def->graphics) - ADD_ARG_LIT("-nographic"); + virCommandAddArg(cmd, "-nographic"); if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { if (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) - ADD_ARG_LIT("-nodefconfig"); /* Disabling global config files */ - ADD_ARG_LIT("-nodefaults"); /* Disabling default guest devices */ + virCommandAddArg(cmd, + "-nodefconfig"); /* Disable global config files */ + virCommandAddArg(cmd, + "-nodefaults"); /* Disable default guest devices */ } if (monitor_chr) { @@ -4320,39 +4229,40 @@ int qemudBuildCommandLine(virConnectPtr conn, /* Use -chardev if it's available */ if (qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) { - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(chrdev = qemuBuildChrChardevStr(monitor_chr))) goto error; - ADD_ARG(chrdev); + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); - ADD_ARG_LIT("-mon"); - if (monitor_json) - ADD_ARG_LIT("chardev=monitor,mode=control"); - else - ADD_ARG_LIT("chardev=monitor,mode=readline"); + virCommandAddArg(cmd, "-mon"); + virCommandAddArgFormat(cmd, "chardev=monitor,mode=%s", + monitor_json ? "control" : "readline"); } else { const char *prefix = NULL; if (monitor_json) prefix = "control,"; - ADD_ARG_LIT("-monitor"); + virCommandAddArg(cmd, "-monitor"); if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) goto error; - ADD_ARG(chrdev); + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); } } if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC) { const char *rtcopt; - ADD_ARG_LIT("-rtc"); + virCommandAddArg(cmd, "-rtc"); if (!(rtcopt = qemuBuildClockArgStr(&def->clock))) goto error; - ADD_ARG(rtcopt); + virCommandAddArg(cmd, rtcopt); + VIR_FREE(rtcopt); } else { switch (def->clock.offset) { case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: - ADD_ARG_LIT("-localtime"); + virCommandAddArg(cmd, "-localtime"); break; case VIR_DOMAIN_CLOCK_OFFSET_UTC: @@ -4368,7 +4278,7 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE && def->clock.data.timezone) { - ADD_ENV_PAIR("TZ", def->clock.data.timezone); + virCommandAddEnvPair(cmd, "TZ", def->clock.data.timezone); } for (i = 0; i < def->clock.ntimers; i++) { @@ -4392,7 +4302,7 @@ int qemudBuildCommandLine(virConnectPtr conn, /* the default - do nothing */ break; case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - ADD_ARG_LIT("-rtc-td-hack"); + virCommandAddArg(cmd, "-rtc-td-hack"); break; case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: @@ -4421,14 +4331,14 @@ int qemudBuildCommandLine(virConnectPtr conn, /* delay is the default if we don't have kernel (-no-kvm-pit), otherwise, the default is catchup. */ if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) - ADD_ARG_LIT("-no-kvm-pit-reinjection"); + virCommandAddArg(cmd, "-no-kvm-pit-reinjection"); break; case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) { /* do nothing - this is default for kvm-pit */ } else if (qemuCmdFlags & QEMUD_CMD_FLAG_TDF) { /* -tdf switches to 'catchup' with userspace pit. */ - ADD_ARG_LIT("-tdf"); + virCommandAddArg(cmd, "-tdf"); } else { /* can't catchup if we have neither pit mode */ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -4457,7 +4367,7 @@ int qemudBuildCommandLine(virConnectPtr conn, if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_HPET) { if (def->clock.timers[i]->present == 0) - ADD_ARG_LIT("-no-hpet"); + virCommandAddArg(cmd, "-no-hpet"); } else { /* no hpet timer available. The only possible action is to raise an error if present="yes" */ @@ -4472,10 +4382,10 @@ int qemudBuildCommandLine(virConnectPtr conn, if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) - ADD_ARG_LIT("-no-reboot"); + virCommandAddArg(cmd, "-no-reboot"); if (!(def->features & (1 << VIR_DOMAIN_FEATURE_ACPI))) - ADD_ARG_LIT("-no-acpi"); + virCommandAddArg(cmd, "-no-acpi"); if (!def->os.bootloader) { for (i = 0 ; i < def->os.nBootDevs ; i++) { @@ -4499,7 +4409,8 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (def->os.nBootDevs) { virBuffer boot_buf = VIR_BUFFER_INITIALIZER; - ADD_ARG_LIT("-boot"); + char *bootstr; + virCommandAddArg(cmd, "-boot"); boot[def->os.nBootDevs] = '\0'; @@ -4518,24 +4429,19 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG(virBufferContentAndReset(&boot_buf)); + bootstr = virBufferContentAndReset(&boot_buf); + virCommandAddArg(cmd, bootstr); + VIR_FREE(bootstr); } - if (def->os.kernel) { - ADD_ARG_LIT("-kernel"); - ADD_ARG_LIT(def->os.kernel); - } - if (def->os.initrd) { - ADD_ARG_LIT("-initrd"); - ADD_ARG_LIT(def->os.initrd); - } - if (def->os.cmdline) { - ADD_ARG_LIT("-append"); - ADD_ARG_LIT(def->os.cmdline); - } + if (def->os.kernel) + virCommandAddArgList(cmd, "-kernel", def->os.kernel, NULL); + if (def->os.initrd) + virCommandAddArgList(cmd, "-initrd", def->os.initrd, NULL); + if (def->os.cmdline) + virCommandAddArgList(cmd, "-append", def->os.cmdline, NULL); } else { - ADD_ARG_LIT("-bootloader"); - ADD_ARG_LIT(def->os.bootloader); + virCommandAddArgList(cmd, "-bootloader", def->os.bootloader, NULL); } for (i = 0 ; i < def->ndisks ; i++) { @@ -4568,13 +4474,14 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); char *devstr; if (!(devstr = qemuBuildControllerDevStr(def->controllers[i]))) goto no_memory; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } @@ -4610,10 +4517,12 @@ int qemudBuildCommandLine(virConnectPtr conn, if ((disk->bus == VIR_DOMAIN_DISK_BUS_USB) && !(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - ADD_USBDISK(disk->src); + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArgFormat(cmd, "disk:%s", disk->src); } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported usb disk type for '%s'"), disk->src); + _("unsupported usb disk type for '%s'"), + disk->src); goto error; } continue; @@ -4634,10 +4543,10 @@ int qemudBuildCommandLine(virConnectPtr conn, break; } - ADD_ARG_LIT("-drive"); + virCommandAddArg(cmd, "-drive"); - /* Unfortunately it is nt possible to use - -device for floppys, or Xen paravirt + /* Unfortunately it is not possible to use + -device for floppies, or Xen paravirt devices. Fortunately, those don't need static PCI addresses, so we don't really care that we can't use -device */ @@ -4648,24 +4557,23 @@ int qemudBuildCommandLine(virConnectPtr conn, (withDeviceArg ? qemuCmdFlags : (qemuCmdFlags & ~QEMUD_CMD_FLAG_DEVICE))))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); if (withDeviceArg) { if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) { - char *fdc; - ADD_ARG_LIT("-global"); - - if (virAsprintf(&fdc, "isa-fdc.drive%c=drive-%s", - disk->info.addr.drive.unit ? 'B' : 'A', - disk->info.alias) < 0) - goto no_memory; - ADD_ARG(fdc); + virCommandAddArg(cmd, "-global"); + virCommandAddArgFormat(cmd, "isa-fdc.drive%c=drive-%s", + disk->info.addr.drive.unit + ? 'B' : 'A', + disk->info.alias); } else { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(optstr = qemuBuildDriveDevStr(disk))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } } } @@ -4677,10 +4585,12 @@ int qemudBuildCommandLine(virConnectPtr conn, if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - ADD_USBDISK(disk->src); + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArgFormat(cmd, "disk:%s", disk->src); } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported usb disk type for '%s'"), disk->src); + _("unsupported usb disk type for '%s'"), + disk->src); goto error; } continue; @@ -4726,8 +4636,7 @@ int qemudBuildCommandLine(virConnectPtr conn, snprintf(file, PATH_MAX, "%s", disk->src); } - ADD_ARG_LIT(dev); - ADD_ARG_LIT(file); + virCommandAddArgList(cmd, dev, file, NULL); } } @@ -4736,15 +4645,17 @@ int qemudBuildCommandLine(virConnectPtr conn, char *optstr; virDomainFSDefPtr fs = def->fss[i]; - ADD_ARG_LIT("-fsdev"); + virCommandAddArg(cmd, "-fsdev"); if (!(optstr = qemuBuildFSStr(fs, qemuCmdFlags))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(optstr = qemuBuildFSDevStr(fs))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } } else { if (def->nfss) { @@ -4756,10 +4667,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!def->nnets) { /* If we have -device, then we set -nodefault already */ - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-net"); - ADD_ARG_LIT("none"); - } + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + virCommandAddArgList(cmd, "-net", "none", NULL); } else { for (i = 0 ; i < def->nnets ; i++) { virDomainNetDefPtr net = def->nets[i]; @@ -4777,21 +4686,16 @@ int qemudBuildCommandLine(virConnectPtr conn, if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { - int tapfd = qemudNetworkIfaceConnect(conn, driver, net, qemuCmdFlags); + int tapfd = qemudNetworkIfaceConnect(conn, driver, net, + qemuCmdFlags); if (tapfd < 0) goto error; - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - virDomainConfNWFilterTeardown(net); - VIR_FORCE_CLOSE(tapfd); - goto no_memory; - } - last_good_net = i; + virCommandTransferFD(cmd, tapfd); - (*vmfds)[(*nvmfds)++] = tapfd; - - if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", tapfd) >= sizeof(tapfd_name)) + if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", + tapfd) >= sizeof(tapfd_name)) goto no_memory; } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { int tapfd = qemudPhysIfaceConnect(conn, driver, net, @@ -4800,17 +4704,11 @@ int qemudBuildCommandLine(virConnectPtr conn, if (tapfd < 0) goto error; - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - virDomainConfNWFilterTeardown(net); - VIR_FORCE_CLOSE(tapfd); - goto no_memory; - } - last_good_net = i; + virCommandTransferFD(cmd, tapfd); - (*vmfds)[(*nvmfds)++] = tapfd; - - if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", tapfd) >= sizeof(tapfd_name)) + if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", + tapfd) >= sizeof(tapfd_name)) goto no_memory; } @@ -4821,14 +4719,10 @@ int qemudBuildCommandLine(virConnectPtr conn, network device */ int vhostfd = qemudOpenVhostNet(net, qemuCmdFlags); if (vhostfd >= 0) { - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - VIR_FORCE_CLOSE(vhostfd); - goto no_memory; - } + virCommandTransferFD(cmd, vhostfd); - (*vmfds)[(*nvmfds)++] = vhostfd; - if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d", vhostfd) - >= sizeof(vhostfd_name)) + if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d", + vhostfd) >= sizeof(vhostfd_name)) goto no_memory; } } @@ -4842,40 +4736,42 @@ int qemudBuildCommandLine(virConnectPtr conn, */ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-netdev"); + virCommandAddArg(cmd, "-netdev"); if (!(host = qemuBuildHostNetStr(net, ',', vlan, tapfd_name, vhostfd_name))) goto error; - ADD_ARG(host); + virCommandAddArg(cmd, host); + VIR_FREE(host); } if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(nic = qemuBuildNicDevStr(net, vlan))) goto error; - ADD_ARG(nic); + virCommandAddArg(cmd, nic); + VIR_FREE(nic); } else { - ADD_ARG_LIT("-net"); + virCommandAddArg(cmd, "-net"); if (!(nic = qemuBuildNicStr(net, "nic,", vlan))) goto error; - ADD_ARG(nic); + virCommandAddArg(cmd, nic); + VIR_FREE(nic); } if (!((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))) { - ADD_ARG_LIT("-net"); + virCommandAddArg(cmd, "-net"); if (!(host = qemuBuildHostNetStr(net, ',', vlan, tapfd_name, vhostfd_name))) goto error; - ADD_ARG(host); + virCommandAddArg(cmd, host); + VIR_FREE(host); } } } if (!def->nserials) { /* If we have -device, then we set -nodefault already */ - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-serial"); - ADD_ARG_LIT("none"); - } + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + virCommandAddArgList(cmd, "-serial", "none", NULL); } else { for (i = 0 ; i < def->nserials ; i++) { virDomainChrDefPtr serial = def->serials[i]; @@ -4884,30 +4780,29 @@ int qemudBuildCommandLine(virConnectPtr conn, /* Use -chardev with -device if they are available */ if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(serial))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); - if (virAsprintf(&devstr, "isa-serial,chardev=%s", serial->info.alias) < 0) - goto no_memory; - ADD_ARG(devstr); + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "isa-serial,chardev=%s", + serial->info.alias); } else { - ADD_ARG_LIT("-serial"); + virCommandAddArg(cmd, "-serial"); if (!(devstr = qemuBuildChrArgStr(serial, NULL))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } } if (!def->nparallels) { /* If we have -device, then we set -nodefault already */ - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-parallel"); - ADD_ARG_LIT("none"); - } + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + virCommandAddArgList(cmd, "-parallel", "none", NULL); } else { for (i = 0 ; i < def->nparallels ; i++) { virDomainChrDefPtr parallel = def->parallels[i]; @@ -4916,20 +4811,21 @@ int qemudBuildCommandLine(virConnectPtr conn, /* Use -chardev with -device if they are available */ if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(parallel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); - if (virAsprintf(&devstr, "isa-parallel,chardev=%s", parallel->info.alias) < 0) - goto no_memory; - ADD_ARG(devstr); + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "isa-parallel,chardev=%s", + parallel->info.alias); } else { - ADD_ARG_LIT("-parallel"); + virCommandAddArg(cmd, "-parallel"); if (!(devstr = qemuBuildChrArgStr(parallel, NULL))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } } @@ -4947,24 +4843,23 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(channel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); char *addr = virSocketFormatAddr(channel->target.addr); if (!addr) goto error; int port = virSocketGetPort(channel->target.addr); - ADD_ARG_LIT("-netdev"); - if (virAsprintf(&devstr, "user,guestfwd=tcp:%s:%i,chardev=%s,id=user-%s", - addr, port, channel->info.alias, channel->info.alias) < 0) { - VIR_FREE(addr); - goto no_memory; - } + virCommandAddArg(cmd, "-netdev"); + virCommandAddArgFormat(cmd, + "user,guestfwd=tcp:%s:%i,chardev=%s,id=user-%s", + addr, port, channel->info.alias, + channel->info.alias); VIR_FREE(addr); - ADD_ARG(devstr); break; case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: @@ -4974,15 +4869,17 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(channel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); break; } } @@ -5000,15 +4897,17 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(console))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(devstr = qemuBuildVirtioSerialPortDevStr(console))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); break; case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL: @@ -5022,20 +4921,22 @@ int qemudBuildCommandLine(virConnectPtr conn, } } - ADD_ARG_LIT("-usb"); + virCommandAddArg(cmd, "-usb"); for (i = 0 ; i < def->ninputs ; i++) { virDomainInputDefPtr input = def->inputs[i]; if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) { if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { char *optstr; - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(optstr = qemuBuildUSBInputDevStr(input))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } else { - ADD_ARG_LIT("-usbdevice"); - ADD_ARG_LIT(input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? "mouse" : "tablet"); + virCommandAddArgList(cmd, "-usbdevice", + input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE + ? "mouse" : "tablet", NULL); } } } @@ -5079,7 +4980,8 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferAddLit(&opt, ",sasl"); if (driver->vncSASLdir) - ADD_ENV_PAIR("SASL_CONF_DIR", driver->vncSASLdir); + virCommandAddEnvPair(cmd, "SASL_CONF_DIR", + driver->vncSASLdir); /* TODO: Support ACLs later */ } @@ -5094,11 +4996,11 @@ int qemudBuildCommandLine(virConnectPtr conn, optstr = virBufferContentAndReset(&opt); - ADD_ARG_LIT("-vnc"); - ADD_ARG(optstr); + virCommandAddArgList(cmd, "-vnc", optstr, NULL); + VIR_FREE(optstr); if (def->graphics[0]->data.vnc.keymap) { - ADD_ARG_LIT("-k"); - ADD_ARG_LIT(def->graphics[0]->data.vnc.keymap); + virCommandAddArgList(cmd, "-k", def->graphics[0]->data.vnc.keymap, + NULL); } /* Unless user requested it, set the audio backend to none, to @@ -5106,45 +5008,33 @@ int qemudBuildCommandLine(virConnectPtr conn, * security issues and might not work when using VNC. */ if (driver->vncAllowHostAudio) { - ADD_ENV_COPY("QEMU_AUDIO_DRV"); + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); } else { - ADD_ENV_LIT("QEMU_AUDIO_DRV=none"); + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); } } else if ((def->ngraphics == 1) && def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - char *xauth = NULL; - char *display = NULL; - - if (def->graphics[0]->data.sdl.xauth && - virAsprintf(&xauth, "XAUTHORITY=%s", - def->graphics[0]->data.sdl.xauth) < 0) - goto no_memory; - if (def->graphics[0]->data.sdl.display && - virAsprintf(&display, "DISPLAY=%s", - def->graphics[0]->data.sdl.display) < 0) { - VIR_FREE(xauth); - goto no_memory; - } - - if (xauth) - ADD_ENV(xauth); - if (display) - ADD_ENV(display); + if (def->graphics[0]->data.sdl.xauth) + virCommandAddEnvPair(cmd, "XAUTHORITY", + def->graphics[0]->data.sdl.xauth); + if (def->graphics[0]->data.sdl.display) + virCommandAddEnvPair(cmd, "DISPLAY", + def->graphics[0]->data.sdl.display); if (def->graphics[0]->data.sdl.fullscreen) - ADD_ARG_LIT("-full-screen"); + virCommandAddArg(cmd, "-full-screen"); /* If using SDL for video, then we should just let it * use QEMU's host audio drivers, possibly SDL too * User can set these two before starting libvirtd */ - ADD_ENV_COPY("QEMU_AUDIO_DRV"); - ADD_ENV_COPY("SDL_AUDIODRIVER"); + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); + virCommandAddEnvPass(cmd, "SDL_AUDIODRIVER"); /* New QEMU has this flag to let us explicitly ask for * SDL graphics. This is better than relying on the * default, since the default changes :-( */ if (qemuCmdFlags & QEMUD_CMD_FLAG_SDL) - ADD_ARG_LIT("-sdl"); + virCommandAddArg(cmd, "-sdl"); } else if ((def->ngraphics == 1) && def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { @@ -5197,16 +5087,15 @@ int qemudBuildCommandLine(virConnectPtr conn, optstr = virBufferContentAndReset(&opt); - ADD_ARG_LIT("-spice"); - ADD_ARG(optstr); - if (def->graphics[0]->data.spice.keymap) { - ADD_ARG_LIT("-k"); - ADD_ARG_LIT(def->graphics[0]->data.spice.keymap); - } + virCommandAddArgList(cmd, "-spice", optstr, NULL); + VIR_FREE(optstr); + if (def->graphics[0]->data.spice.keymap) + virCommandAddArgList(cmd, "-k", + def->graphics[0]->data.spice.keymap, NULL); /* SPICE includes native support for tunnelling audio, so we * set the audio backend to point at SPICE's own driver */ - ADD_ENV_LIT("QEMU_AUDIO_DRV=spice"); + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice"); } else if ((def->ngraphics == 1)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -5235,18 +5124,17 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-vga"); - ADD_ARG_LIT(vgastr); + virCommandAddArgList(cmd, "-vga", vgastr, NULL); } } else { switch (def->videos[0]->type) { case VIR_DOMAIN_VIDEO_TYPE_VGA: - ADD_ARG_LIT("-std-vga"); + virCommandAddArg(cmd, "-std-vga"); break; case VIR_DOMAIN_VIDEO_TYPE_VMVGA: - ADD_ARG_LIT("-vmwarevga"); + virCommandAddArg(cmd, "-vmwarevga"); break; case VIR_DOMAIN_VIDEO_TYPE_XEN: @@ -5273,12 +5161,13 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(str = qemuBuildVideoDevStr(def->videos[i]))) goto error; - ADD_ARG(str); + virCommandAddArg(cmd, str); + VIR_FREE(str); } } else { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -5290,10 +5179,8 @@ int qemudBuildCommandLine(virConnectPtr conn, } else { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - (qemuCmdFlags & QEMUD_CMD_FLAG_VGA)) { - ADD_ARG_LIT("-vga"); - ADD_ARG_LIT("none"); - } + (qemuCmdFlags & QEMUD_CMD_FLAG_VGA)) + virCommandAddArgList(cmd, "-vga", "none", NULL); } /* Add sound hardware */ @@ -5307,15 +5194,15 @@ int qemudBuildCommandLine(virConnectPtr conn, * we don't need to set any PCI address on it, so we don't * mind too much */ if (sound->model == VIR_DOMAIN_SOUND_MODEL_PCSPK) { - ADD_ARG_LIT("-soundhw"); - ADD_ARG_LIT("pcspk"); + virCommandAddArgList(cmd, "-soundhw", "pcspk", NULL); } else { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(str = qemuBuildSoundDevStr(sound))) goto error; - ADD_ARG(str); + virCommandAddArg(cmd, str); + VIR_FREE(str); } } } else { @@ -5338,8 +5225,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (i < (def->nsounds - 1)) strncat(modstr, ",", size--); } - ADD_ARG_LIT("-soundhw"); - ADD_ARG(modstr); + virCommandAddArgList(cmd, "-soundhw", modstr, NULL); + VIR_FREE(modstr); } } @@ -5349,13 +5236,13 @@ int qemudBuildCommandLine(virConnectPtr conn, char *optstr; if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); optstr = qemuBuildWatchdogDevStr(watchdog); if (!optstr) goto error; } else { - ADD_ARG_LIT("-watchdog"); + virCommandAddArg(cmd, "-watchdog"); const char *model = virDomainWatchdogModelTypeToString(watchdog->model); if (!model) { @@ -5367,7 +5254,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!(optstr = strdup(model))) goto no_memory; } - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); const char *action = virDomainWatchdogActionTypeToString(watchdog->action); if (!action) { @@ -5375,8 +5263,7 @@ int qemudBuildCommandLine(virConnectPtr conn, "%s", _("invalid watchdog action")); goto error; } - ADD_ARG_LIT("-watchdog-action"); - ADD_ARG_LIT(action); + virCommandAddArgList(cmd, "-watchdog-action", action, NULL); } /* Add host passthrough hardware */ @@ -5389,15 +5276,17 @@ int qemudBuildCommandLine(virConnectPtr conn, hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else { - ADD_ARG_LIT("-usbdevice"); + virCommandAddArg(cmd, "-usbdevice"); if (!(devstr = qemuBuildUSBHostdevUsbDevStr(hostdev))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } @@ -5416,26 +5305,22 @@ int qemudBuildCommandLine(virConnectPtr conn, goto no_memory; } - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - VIR_FREE(configfd_name); - VIR_FORCE_CLOSE(configfd); - goto no_memory; - } - - (*vmfds)[(*nvmfds)++] = configfd; + virCommandTransferFD(cmd, configfd); } } - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name); VIR_FREE(configfd_name); if (!devstr) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) { - ADD_ARG_LIT("-pcidevice"); + virCommandAddArg(cmd, "-pcidevice"); if (!(devstr = qemuBuildPCIHostdevPCIDevStr(hostdev))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("PCI device assignment is not supported by this version of qemu")); @@ -5444,10 +5329,8 @@ int qemudBuildCommandLine(virConnectPtr conn, } } - if (migrateFrom) { - ADD_ARG_LIT("-incoming"); - ADD_ARG_LIT(migrateFrom); - } + if (migrateFrom) + virCommandAddArgList(cmd, "-incoming", migrateFrom, NULL); /* QEMU changed its default behavior to not include the virtio balloon * device. Explicitly request it to ensure it will be present. @@ -5465,76 +5348,43 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { char *optstr; - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); optstr = qemuBuildMemballoonDevStr(def->memballoon); if (!optstr) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } else if (qemuCmdFlags & QEMUD_CMD_FLAG_BALLOON) { - ADD_ARG_LIT("-balloon"); - ADD_ARG_LIT("virtio"); + virCommandAddArgList(cmd, "-balloon", "virtio", NULL); } } - if (current_snapshot && current_snapshot->def->active) { - ADD_ARG_LIT("-loadvm"); - ADD_ARG_LIT(current_snapshot->def->name); - } + if (current_snapshot && current_snapshot->def->active) + virCommandAddArgList(cmd, "-loadvm", current_snapshot->def->name, + NULL); if (def->namespaceData) { - qemuDomainCmdlineDefPtr cmd; - - cmd = def->namespaceData; - for (i = 0; i < cmd->num_args; i++) - ADD_ARG_LIT(cmd->args[i]); - for (i = 0; i < cmd->num_env; i++) { - if (cmd->env_value[i]) - ADD_ENV_PAIR(cmd->env_name[i], cmd->env_value[i]); - else - ADD_ENV_PAIR(cmd->env_name[i], ""); - } - } + qemuDomainCmdlineDefPtr qemucmd; - ADD_ARG(NULL); - ADD_ENV(NULL); + qemucmd = def->namespaceData; + for (i = 0; i < qemucmd->num_args; i++) + virCommandAddArg(cmd, qemucmd->args[i]); + for (i = 0; i < qemucmd->num_env; i++) + virCommandAddEnvPair(cmd, qemucmd->env_name[i], + qemucmd->env_value[i] + ? qemucmd->env_value[i] : ""); + } - *retargv = qargv; - *retenv = qenv; - return 0; + return cmd; no_memory: virReportOOMError(); error: for (i = 0; i <= last_good_net; i++) virDomainConfNWFilterTeardown(def->nets[i]); - if (vmfds && - *vmfds) { - for (i = 0; i < *nvmfds; i++) - VIR_FORCE_CLOSE((*vmfds)[i]); - VIR_FREE(*vmfds); - *nvmfds = 0; - } - if (qargv) { - for (i = 0 ; i < qargc ; i++) - VIR_FREE((qargv)[i]); - VIR_FREE(qargv); - } - if (qenv) { - for (i = 0 ; i < qenvc ; i++) - VIR_FREE((qenv)[i]); - VIR_FREE(qenv); - } - return -1; - -#undef ADD_ARG -#undef ADD_ARG_LIT -#undef ADD_ARG_SPACE -#undef ADD_USBDISK -#undef ADD_ENV -#undef ADD_ENV_COPY -#undef ADD_ENV_LIT -#undef ADD_ENV_SPACE + virCommandFree(cmd); + return NULL; } diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 790ce98..24df871 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -25,6 +25,7 @@ # define __QEMUD_CONF_H # include +# include # include "ebtables.h" # include "internal.h" @@ -40,6 +41,7 @@ # include "cpu_conf.h" # include "driver.h" # include "bitmap.h" +# include "command.h" # define qemudDebug(fmt, ...) do {} while(0) @@ -227,16 +229,12 @@ int qemudParseHelpStr (const char *qemu, unsigned int *is_kvm, unsigned int *kvm_version); -int qemudBuildCommandLine (virConnectPtr conn, +virCommandPtr qemudBuildCommandLine (virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, virDomainChrDefPtr monitor_chr, - int monitor_json, + bool monitor_json, unsigned long long qemuCmdFlags, - const char ***retargv, - const char ***retenv, - int **vmfds, - int *nvmfds, const char *migrateFrom, virDomainSnapshotObjPtr current_snapshot) ATTRIBUTE_NONNULL(1); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f00d8a3..38d7bd2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3018,14 +3018,6 @@ qemuAssignPCIAddresses(virDomainDefPtr def) int ret = -1; unsigned long long qemuCmdFlags = 0; qemuDomainPCIAddressSetPtr addrs = NULL; - struct stat sb; - - if (stat(def->emulator, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), - def->emulator); - goto cleanup; - } if (qemudExtractVersionInfo(def->emulator, NULL, @@ -3865,30 +3857,21 @@ static int qemudStartVMDaemon(virConnectPtr conn, bool start_paused, int stdin_fd, const char *stdin_path) { - const char **argv = NULL, **tmp; - const char **progenv = NULL; - int i, ret, runflags; - struct stat sb; - int *vmfds = NULL; - int nvmfds = 0; + int ret; unsigned long long qemuCmdFlags; - fd_set keepfd; - const char *emulator; - pid_t child; int pos = -1; char ebuf[1024]; char *pidfile = NULL; int logfile = -1; char *timestamp; qemuDomainObjPrivatePtr priv = vm->privateData; + virCommandPtr cmd = NULL; struct qemudHookData hookData; hookData.conn = conn; hookData.vm = vm; hookData.driver = driver; - FD_ZERO(&keepfd); - DEBUG0("Beginning VM startup process"); if (virDomainObjIsActive(vm)) { @@ -3979,21 +3962,8 @@ static int qemudStartVMDaemon(virConnectPtr conn, if ((logfile = qemudLogFD(driver, vm->def->name, false)) < 0) goto cleanup; - emulator = vm->def->emulator; - - /* Make sure the binary we are about to try exec'ing exists. - * Technically we could catch the exec() failure, but that's - * in a sub-process so its hard to feed back a useful error - */ - if (stat(emulator, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), - emulator); - goto cleanup; - } - - DEBUG0("Determing emulator version"); - if (qemudExtractVersionInfo(emulator, + DEBUG0("Determining emulator version"); + if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) goto cleanup; @@ -4062,10 +4032,10 @@ static int qemudStartVMDaemon(virConnectPtr conn, DEBUG0("Building emulator command line"); vm->def->id = driver->nextvmid++; - if (qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, - priv->monJSON, qemuCmdFlags, &argv, &progenv, - &vmfds, &nvmfds, migrateFrom, - vm->current_snapshot) < 0) + if (!(cmd = qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, + priv->monJSON != 0, qemuCmdFlags, + migrateFrom, + vm->current_snapshot))) goto cleanup; if (qemuDomainSnapshotSetInactive(vm, driver->snapshotDir) < 0) @@ -4100,50 +4070,28 @@ static int qemudStartVMDaemon(virConnectPtr conn, VIR_FREE(timestamp); } - tmp = progenv; - - while (*tmp) { - if (safewrite(logfile, *tmp, strlen(*tmp)) < 0) - VIR_WARN("Unable to write envv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - if (safewrite(logfile, " ", 1) < 0) - VIR_WARN("Unable to write envv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - tmp++; - } - tmp = argv; - while (*tmp) { - if (safewrite(logfile, *tmp, strlen(*tmp)) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - if (safewrite(logfile, " ", 1) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - tmp++; - } - if (safewrite(logfile, "\n", 1) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); + virCommandWriteArgLog(cmd, logfile); if ((pos = lseek(logfile, 0, SEEK_END)) < 0) VIR_WARN("Unable to seek to end of logfile: %s", virStrerror(errno, ebuf, sizeof ebuf)); - for (i = 0 ; i < nvmfds ; i++) - FD_SET(vmfds[i], &keepfd); - VIR_DEBUG("Clear emulator capabilities: %d", driver->clearEmulatorCapabilities); - runflags = VIR_EXEC_NONBLOCK; - if (driver->clearEmulatorCapabilities) { - runflags |= VIR_EXEC_CLEAR_CAPS; - } - - ret = virExecDaemonize(argv, progenv, &keepfd, &child, - stdin_fd, &logfile, &logfile, - runflags, - qemudSecurityHook, &hookData, - pidfile); + if (driver->clearEmulatorCapabilities) + virCommandClearCaps(cmd); + + VIR_WARN("Executing %s", vm->def->emulator); + virCommandSetPreExecHook(cmd, qemudSecurityHook, &hookData); + virCommandSetInputFD(cmd, stdin_fd); + virCommandSetOutputFD(cmd, &logfile); + virCommandSetErrorFD(cmd, &logfile); + virCommandNonblockingFDs(cmd); + virCommandSetPidFile(cmd, pidfile); + virCommandDaemonize(cmd); + + ret = virCommandRun(cmd, NULL); + VIR_WARN("Executing done %s", vm->def->emulator); VIR_FREE(pidfile); /* wait for qemu process to to show up */ @@ -4153,7 +4101,13 @@ static int qemudStartVMDaemon(virConnectPtr conn, _("Domain %s didn't show up\n"), vm->def->name); ret = -1; } +#if 0 } else if (ret == -2) { + /* + * XXX this is bogus. It isn't safe to set vm->pid = child + * because the child no longer exists. + */ + /* The virExec process that launches the daemon failed. Pending on * when it failed (we can't determine for sure), there may be * extra info in the domain log (if the hook failed for example). @@ -4163,30 +4117,16 @@ static int qemudStartVMDaemon(virConnectPtr conn, */ vm->pid = child; ret = 0; +#endif } if (migrateFrom) start_paused = true; vm->state = start_paused ? VIR_DOMAIN_PAUSED : VIR_DOMAIN_RUNNING; - for (i = 0 ; argv[i] ; i++) - VIR_FREE(argv[i]); - VIR_FREE(argv); - - for (i = 0 ; progenv[i] ; i++) - VIR_FREE(progenv[i]); - VIR_FREE(progenv); - if (ret == -1) /* The VM failed to start; tear filters before taps */ virDomainConfVMNWFilterTeardown(vm); - if (vmfds) { - for (i = 0 ; i < nvmfds ; i++) { - VIR_FORCE_CLOSE(vmfds[i]); - } - VIR_FREE(vmfds); - } - if (ret == -1) /* The VM failed to start */ goto cleanup; @@ -4240,6 +4180,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (virDomainObjSetDefTransient(driver->caps, vm) < 0) goto cleanup; + virCommandFree(cmd); VIR_FORCE_CLOSE(logfile); return 0; @@ -4248,9 +4189,9 @@ cleanup: /* We jump here if we failed to start the VM for any reason, or * if we failed to initialize the now running VM. kill it off and * pretend we never started it */ - qemudShutdownVMDaemon(driver, vm, 0); - + virCommandFree(cmd); VIR_FORCE_CLOSE(logfile); + qemudShutdownVMDaemon(driver, vm, 0); return -1; } @@ -7312,13 +7253,8 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, struct qemud_driver *driver = conn->privateData; virDomainDefPtr def = NULL; virDomainChrDef monConfig; - const char *emulator; unsigned long long qemuCmdFlags; - struct stat sb; - const char **retargv = NULL; - const char **retenv = NULL; - const char **tmp; - virBuffer buf = VIR_BUFFER_INITIALIZER; + virCommandPtr cmd = NULL; char *ret = NULL; int i; @@ -7368,70 +7304,26 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, def->graphics[i]->data.vnc.autoport) def->graphics[i]->data.vnc.port = 5900; } - emulator = def->emulator; - - /* Make sure the binary we are about to try exec'ing exists. - * Technically we could catch the exec() failure, but that's - * in a sub-process so its hard to feed back a useful error - */ - if (stat(emulator, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), - emulator); - goto cleanup; - } - if (qemudExtractVersionInfo(emulator, + if (qemudExtractVersionInfo(def->emulator, NULL, - &qemuCmdFlags) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Cannot determine QEMU argv syntax %s"), - emulator); + &qemuCmdFlags) < 0) goto cleanup; - } if (qemuPrepareMonitorChr(driver, &monConfig, def->name) < 0) goto cleanup; - if (qemudBuildCommandLine(conn, driver, def, - &monConfig, 0, qemuCmdFlags, - &retargv, &retenv, - NULL, NULL, /* Don't want it to create TAP devices */ - NULL, NULL) < 0) { - goto cleanup; - } - - tmp = retenv; - while (*tmp) { - virBufferAdd(&buf, *tmp, strlen(*tmp)); - virBufferAddLit(&buf, " "); - tmp++; - } - tmp = retargv; - while (*tmp) { - virBufferAdd(&buf, *tmp, strlen(*tmp)); - virBufferAddLit(&buf, " "); - tmp++; - } - - if (virBufferError(&buf)) { - virBufferFreeAndReset(&buf); - virReportOOMError(); + if (!(cmd = qemudBuildCommandLine(conn, driver, def, + &monConfig, false, qemuCmdFlags, + NULL, NULL))) goto cleanup; - } - ret = virBufferContentAndReset(&buf); + ret = virCommandToString(cmd); cleanup: qemuDriverUnlock(driver); - for (tmp = retargv ; tmp && *tmp ; tmp++) - VIR_FREE(*tmp); - VIR_FREE(retargv); - - for (tmp = retenv ; tmp && *tmp ; tmp++) - VIR_FREE(*tmp); - VIR_FREE(retenv); + virCommandFree(cmd); virDomainDefFree(def); return ret; } diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index 947a6d8..55b3ef6 100644 --- a/src/uml/uml_conf.c +++ b/src/uml/uml_conf.c @@ -308,7 +308,7 @@ error: static char * umlBuildCommandLineChr(virDomainChrDefPtr def, const char *dev, - fd_set *keepfd) + virCommandPtr cmd) { char *ret = NULL; @@ -372,7 +372,7 @@ umlBuildCommandLineChr(virDomainChrDefPtr def, VIR_FORCE_CLOSE(fd_out); return NULL; } - FD_SET(fd_out, keepfd); + virCommandTransferFD(cmd, fd_out); } break; case VIR_DOMAIN_CHR_TYPE_PIPE: @@ -425,20 +425,17 @@ virCommandPtr umlBuildCommandLine(virConnectPtr conn, virDomainObjPtr vm) { int i, j; - char memory[50]; struct utsname ut; virCommandPtr cmd; uname(&ut); - snprintf(memory, sizeof(memory), "%luK", vm->def->mem.cur_balloon); - cmd = virCommandNew(vm->def->os.kernel); virCommandAddEnvPassCommon(cmd); //virCommandAddArgPair(cmd, "con0", "fd:0,fd:1"); - virCommandAddArgPair(cmd, "mem", memory); + virCommandAddArgFormat(cmd, "mem=%luK", vm->def->mem.cur_balloon); virCommandAddArgPair(cmd, "umid", vm->def->name); virCommandAddArgPair(cmd, "uml_dir", driver->monitorDir); @@ -468,7 +465,7 @@ virCommandPtr umlBuildCommandLine(virConnectPtr conn, for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) { char *ret = NULL; if (i == 0 && vm->def->console) - ret = umlBuildCommandLineChr(vm->def->console, "con", keepfd); + ret = umlBuildCommandLineChr(vm->def->console, "con", cmd); if (!ret) if (virAsprintf(&ret, "con%d=none", i) < 0) goto no_memory; @@ -483,7 +480,7 @@ virCommandPtr umlBuildCommandLine(virConnectPtr conn, if (vm->def->serials[j]->target.port == i) chr = vm->def->serials[j]; if (chr) - ret = umlBuildCommandLineChr(chr, "ssl", keepfd); + ret = umlBuildCommandLineChr(chr, "ssl", cmd); if (!ret) if (virAsprintf(&ret, "ssl%d=none", i) < 0) goto no_memory; diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 6c28c76..546e960 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -812,18 +812,11 @@ static int umlCleanupTapDevices(virConnectPtr conn ATTRIBUTE_UNUSED, static int umlStartVMDaemon(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm) { - const char **argv = NULL, **tmp; - const char **progenv = NULL; - int i, ret; - pid_t pid; + int ret; char *logfile; int logfd = -1; - struct stat sb; - fd_set keepfd; - char ebuf[1024]; umlDomainObjPrivatePtr priv = vm->privateData; - - FD_ZERO(&keepfd); + virCommandPtr cmd = NULL; if (virDomainObjIsActive(vm)) { umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -840,7 +833,7 @@ static int umlStartVMDaemon(virConnectPtr conn, * Technically we could catch the exec() failure, but that's * in a sub-process so its hard to feed back a useful error */ - if (stat(vm->def->os.kernel, &sb) < 0) { + if (access(vm->def->os.kernel, X_OK) < 0) { virReportSystemError(errno, _("Cannot find UML kernel %s"), vm->def->os.kernel); @@ -877,67 +870,30 @@ static int umlStartVMDaemon(virConnectPtr conn, return -1; } - if (umlBuildCommandLine(conn, driver, vm, &keepfd, - &argv, &progenv) < 0) { + if (!(cmd = umlBuildCommandLine(conn, driver, vm))) { VIR_FORCE_CLOSE(logfd); virDomainConfVMNWFilterTeardown(vm); umlCleanupTapDevices(conn, vm); return -1; } - tmp = progenv; - while (*tmp) { - if (safewrite(logfd, *tmp, strlen(*tmp)) < 0) - VIR_WARN("Unable to write envv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - if (safewrite(logfd, " ", 1) < 0) - VIR_WARN("Unable to write envv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - tmp++; - } - tmp = argv; - while (*tmp) { - if (safewrite(logfd, *tmp, strlen(*tmp)) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - if (safewrite(logfd, " ", 1) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - tmp++; - } - if (safewrite(logfd, "\n", 1) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); + virCommandWriteArgLog(cmd, logfd); priv->monitor = -1; - ret = virExecDaemonize(argv, progenv, &keepfd, &pid, - -1, &logfd, &logfd, - VIR_EXEC_CLEAR_CAPS, - NULL, NULL, NULL); + virCommandClearCaps(cmd); + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); + virCommandDaemonize(cmd); + + ret = virCommandRun(cmd, NULL); VIR_FORCE_CLOSE(logfd); if (ret < 0) goto cleanup; ret = virDomainObjSetDefTransient(driver->caps, vm); cleanup: - /* - * At the moment, the only thing that populates keepfd is - * umlBuildCommandLineChr. We want to close every fd it opens. - */ - for (i = 0; i < FD_SETSIZE; i++) - if (FD_ISSET(i, &keepfd)) { - int tmpfd = i; - VIR_FORCE_CLOSE(tmpfd); - } - - for (i = 0 ; argv[i] ; i++) - VIR_FREE(argv[i]); - VIR_FREE(argv); - - for (i = 0 ; progenv[i] ; i++) - VIR_FREE(progenv[i]); - VIR_FREE(progenv); + virCommandFree(cmd); if (ret < 0) { virDomainConfVMNWFilterTeardown(vm); diff --git a/src/util/command.c b/src/util/command.c index 71db2a1..948a957 100644 --- a/src/util/command.c +++ b/src/util/command.c @@ -33,6 +33,7 @@ #include "util.h" #include "logging.h" #include "files.h" +#include "buf.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -54,7 +55,9 @@ struct _virCommand { char *pwd; /* XXX Use int[] if we ever need to support more than FD_SETSIZE fd's. */ - fd_set preserve; + fd_set preserve; /* FDs to pass to child. */ + fd_set transfer; /* FDs to close in parent. */ + unsigned int flags; char *inbuf; @@ -99,6 +102,7 @@ virCommandNewArgs(const char *const*args) return NULL; FD_ZERO(&cmd->preserve); + FD_ZERO(&cmd->transfer); cmd->infd = cmd->outfd = cmd->errfd = -1; cmd->inpipe = -1; cmd->pid = -1; @@ -113,27 +117,76 @@ virCommandNewArgs(const char *const*args) return cmd; } +/* + * Create a new command with a NULL terminated + * list of args, starting with the binary to run + */ +virCommandPtr +virCommandNewArgList(const char *binary, ...) +{ + virCommandPtr cmd = virCommandNew(binary); + va_list list; + const char *arg; + + if (!cmd || cmd->has_error) + return NULL; + + va_start(list, binary); + while ((arg = va_arg(list, const char *)) != NULL) + virCommandAddArg(cmd, arg); + va_end(list); + return cmd; +} + /* * Preserve the specified file descriptor in the child, instead of - * closing it. FD must not be one of the three standard streams. + * closing it. FD must not be one of the three standard streams. If + * transfer is true, then fd will be closed in the parent after a call + * to Run/RunAsync/Free, otherwise caller is still responsible for fd. */ -void -virCommandPreserveFD(virCommandPtr cmd, int fd) +static void +virCommandKeepFD(virCommandPtr cmd, int fd, bool transfer) { - if (!cmd || cmd->has_error) + if (!cmd) return; if (fd <= STDERR_FILENO || FD_SETSIZE <= fd) { - cmd->has_error = -1; + if (!cmd->has_error) + cmd->has_error = -1; VIR_DEBUG("cannot preserve %d", fd); return; } FD_SET(fd, &cmd->preserve); + if (transfer) + FD_SET(fd, &cmd->transfer); } /* + * Preserve the specified file descriptor + * in the child, instead of closing it. + * The parent is still responsible for managing fd. + */ +void +virCommandPreserveFD(virCommandPtr cmd, int fd) +{ + return virCommandKeepFD(cmd, fd, false); +} + +/* + * Transfer the specified file descriptor + * to the child, instead of closing it. + * Close the fd in the parent during Run/RunAsync/Free. + */ +void +virCommandTransferFD(virCommandPtr cmd, int fd) +{ + return virCommandKeepFD(cmd, fd, true); +} + + +/* * Save the child PID in a pidfile */ void @@ -192,6 +245,19 @@ virCommandDaemonize(virCommandPtr cmd) } /* + * Set FDs created by virCommandSetOutputFD and virCommandSetErrorFD + * as non-blocking in the parent. + */ +void +virCommandNonblockingFDs(virCommandPtr cmd) +{ + if (!cmd || cmd->has_error) + return; + + cmd->flags |= VIR_EXEC_NONBLOCK; +} + +/* * Add an environment variable to the child * using separate name & value strings */ @@ -317,20 +383,24 @@ virCommandAddArg(virCommandPtr cmd, const char *val) /* - * Add "NAME=VAL" as a single command line argument to the child + * Add a command line argument created by a printf-style format */ void -virCommandAddArgPair(virCommandPtr cmd, const char *name, const char *val) +virCommandAddArgFormat(virCommandPtr cmd, const char *format, ...) { char *arg; + va_list list; if (!cmd || cmd->has_error) return; - if (virAsprintf(&arg, "%s=%s", name, val) < 0) { + va_start(list, format); + if (virVasprintf(&arg, format, list) < 0) { cmd->has_error = ENOMEM; + va_end(list); return; } + va_end(list); /* Arg plus trailing NULL. */ if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) { @@ -343,6 +413,15 @@ virCommandAddArgPair(virCommandPtr cmd, const char *name, const char *val) } /* + * Add "NAME=VAL" as a single command line argument to the child + */ +void +virCommandAddArgPair(virCommandPtr cmd, const char *name, const char *val) +{ + virCommandAddArgFormat(cmd, "%s=%s", name, val); +} + +/* * Add a NULL terminated list of args */ void @@ -569,6 +648,89 @@ virCommandSetPreExecHook(virCommandPtr cmd, virExecHook hook, void *opaque) /* + * Call after adding all arguments and environment settings, but before + * Run/RunAsync, to immediately output the environment and arguments of + * cmd to logfd. If virCommandRun cannot succeed (because of an + * out-of-memory condition while building cmd), nothing will be logged. + */ +void +virCommandWriteArgLog(virCommandPtr cmd, int logfd) +{ + int ioError = 0; + size_t i; + + /* Any errors will be reported later by virCommandRun, which means + * no command will be run, so there is nothing to log. */ + if (!cmd || cmd->has_error) + return; + + for (i = 0 ; i < cmd->nenv ; i++) { + if (safewrite(logfd, cmd->env[i], strlen(cmd->env[i])) < 0) + ioError = errno; + if (safewrite(logfd, " ", 1) < 0) + ioError = errno; + } + for (i = 0 ; i < cmd->nargs ; i++) { + if (safewrite(logfd, cmd->args[i], strlen(cmd->args[i])) < 0) + ioError = errno; + if (safewrite(logfd, i == cmd->nargs - 1 ? "\n" : " ", 1) < 0) + ioError = errno; + } + + if (ioError) { + char ebuf[1024]; + VIR_WARN("Unable to write command %s args to logfile: %s", + cmd->args[0], virStrerror(ioError, ebuf, sizeof ebuf)); + } +} + + +/* + * Call after adding all arguments and environment settings, but before + * Run/RunAsync, to return a string representation of the environment and + * arguments of cmd. If virCommandRun cannot succeed (because of an + * out-of-memory condition while building cmd), NULL will be returned. + * Caller is responsible for freeing the resulting string. + */ +char * +virCommandToString(virCommandPtr cmd) +{ + size_t i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + /* Cannot assume virCommandRun will be called; so report the error + * now. If virCommandRun is called, it will report the same error. */ + if (!cmd || cmd->has_error == -1) { + virCommandError(VIR_ERR_INTERNAL_ERROR, "%s", + _("invalid use of command API")); + return NULL; + } + if (cmd->has_error == ENOMEM) { + virReportOOMError(); + return NULL; + } + + for (i = 0; i < cmd->nenv; i++) { + virBufferAdd(&buf, cmd->env[i], strlen(cmd->env[i])); + virBufferAddChar(&buf, ' '); + } + virBufferAdd(&buf, cmd->args[0], strlen(cmd->args[0])); + for (i = 1; i < cmd->nargs; i++) { + virBufferAddChar(&buf, ' '); + virBufferAdd(&buf, cmd->args[i], strlen(cmd->args[i])); + } + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +/* * Manage input and output to the child process. */ static int @@ -823,6 +985,7 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid) { int ret; char *str; + int i; if (!cmd || cmd->has_error == -1) { virCommandError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -868,6 +1031,14 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid) VIR_DEBUG("Command result %d, with PID %d", ret, (int)cmd->pid); + for (i = STDERR_FILENO + 1; i < FD_SETSIZE; i++) { + if (FD_ISSET(i, &cmd->transfer)) { + int tmpfd = i; + VIR_FORCE_CLOSE(tmpfd); + FD_CLR(i, &cmd->transfer); + } + } + if (ret == 0 && pid) *pid = cmd->pid; @@ -941,6 +1112,13 @@ virCommandFree(virCommandPtr cmd) if (!cmd) return; + for (i = STDERR_FILENO + 1; i < FD_SETSIZE; i++) { + if (FD_ISSET(i, &cmd->transfer)) { + int tmpfd = i; + VIR_FORCE_CLOSE(tmpfd); + } + } + VIR_FORCE_CLOSE(cmd->outfd); VIR_FORCE_CLOSE(cmd->errfd); diff --git a/src/util/command.h b/src/util/command.h index ca24869..9b04e68 100644 --- a/src/util/command.h +++ b/src/util/command.h @@ -39,18 +39,34 @@ virCommandPtr virCommandNew(const char *binary) ATTRIBUTE_NONNULL(1); */ virCommandPtr virCommandNewArgs(const char *const*args) ATTRIBUTE_NONNULL(1); +/* + * Create a new command with a NULL terminated + * list of args, starting with the binary to run + */ +virCommandPtr virCommandNewArgList(const char *binary, ...) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL; + /* All error report from these setup APIs is - * delayed until the Run/Exec/Wait methods + * delayed until the Run/RunAsync methods */ /* * Preserve the specified file descriptor - * in the child, instead of closing it + * in the child, instead of closing it. + * The parent is still responsible for managing fd. */ void virCommandPreserveFD(virCommandPtr cmd, int fd); /* + * Transfer the specified file descriptor + * to the child, instead of closing it. + * Close the fd in the parent during Run/RunAsync/Free. + */ +void virCommandTransferFD(virCommandPtr cmd, + int fd); + +/* * Save the child PID in a pidfile */ void virCommandSetPidFile(virCommandPtr cmd, @@ -74,6 +90,11 @@ void virCommandAllowCap(virCommandPtr cmd, */ void virCommandDaemonize(virCommandPtr cmd); +/* + * Set FDs created by virCommandSetOutputFD and virCommandSetErrorFD + * as non-blocking in the parent. + */ +void virCommandNonblockingFDs(virCommandPtr cmd); /* * Add an environment variable to the child @@ -106,6 +127,14 @@ void virCommandAddEnvPassCommon(virCommandPtr cmd); */ void virCommandAddArg(virCommandPtr cmd, const char *val) ATTRIBUTE_NONNULL(2); + +/* + * Add a command line argument created by a printf-style format + */ +void virCommandAddArgFormat(virCommandPtr cmd, + const char *format, ...) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3); + /* * Add a command line argument to the child */ @@ -179,6 +208,24 @@ void virCommandSetPreExecHook(virCommandPtr cmd, void *opaque) ATTRIBUTE_NONNULL(2); /* + * Call after adding all arguments and environment settings, but before + * Run/RunAsync, to immediately output the environment and arguments of + * cmd to logfd. If virCommandRun cannot succeed (because of an + * out-of-memory condition while building cmd), nothing will be logged. + */ +void virCommandWriteArgLog(virCommandPtr cmd, + int logfd); + +/* + * Call after adding all arguments and environment settings, but before + * Run/RunAsync, to return a string representation of the environment and + * arguments of cmd. If virCommandRun cannot succeed (because of an + * out-of-memory condition while building cmd), NULL will be returned. + * Caller is responsible for freeing the resulting string. + */ +char *virCommandToString(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK; + +/* * Run the command and wait for completion. * Returns -1 on any error executing the * command. Returns 0 if the command executed, diff --git a/src/util/hooks.c b/src/util/hooks.c index 1139d88..5ba2036 100644 --- a/src/util/hooks.c +++ b/src/util/hooks.c @@ -250,12 +250,10 @@ virHookCall(int driver, const char *id, int op, int sub_op, const char *extra, return(-1); } - cmd = virCommandNew(path); + cmd = virCommandNewArgList(path, id, opstr, subopstr, extra, NULL); virCommandAddEnvPassCommon(cmd); - virCommandAddArgList(cmd, id, opstr, subopstr, extra, NULL); - if (input) virCommandSetInputBuffer(cmd, input); diff --git a/src/util/util.c b/src/util/util.c index a2582aa..f6050de 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -88,42 +88,44 @@ verify(sizeof(gid_t) <= sizeof (unsigned int) && __FUNCTION__, __LINE__, __VA_ARGS__) /* Like read(), but restarts after EINTR */ -int saferead(int fd, void *buf, size_t count) -{ - size_t nread = 0; - while (count > 0) { - ssize_t r = read(fd, buf, count); - if (r < 0 && errno == EINTR) - continue; - if (r < 0) - return r; - if (r == 0) - return nread; - buf = (char *)buf + r; - count -= r; - nread += r; - } - return nread; +ssize_t +saferead(int fd, void *buf, size_t count) +{ + size_t nread = 0; + while (count > 0) { + ssize_t r = read(fd, buf, count); + if (r < 0 && errno == EINTR) + continue; + if (r < 0) + return r; + if (r == 0) + return nread; + buf = (char *)buf + r; + count -= r; + nread += r; + } + return nread; } /* Like write(), but restarts after EINTR */ -ssize_t safewrite(int fd, const void *buf, size_t count) -{ - size_t nwritten = 0; - while (count > 0) { - ssize_t r = write(fd, buf, count); - - if (r < 0 && errno == EINTR) - continue; - if (r < 0) - return r; - if (r == 0) - return nwritten; - buf = (const char *)buf + r; - count -= r; - nwritten += r; - } - return nwritten; +ssize_t +safewrite(int fd, const void *buf, size_t count) +{ + size_t nwritten = 0; + while (count > 0) { + ssize_t r = write(fd, buf, count); + + if (r < 0 && errno == EINTR) + continue; + if (r < 0) + return r; + if (r == 0) + return nwritten; + buf = (const char *)buf + r; + count -= r; + nwritten += r; + } + return nwritten; } #ifdef HAVE_POSIX_FALLOCATE @@ -2197,6 +2199,22 @@ virParseVersionString(const char *str, unsigned long *version) } /** + * virVasprintf + * + * like glibc's vasprintf but makes sure *strp == NULL on failure + */ +int +virVasprintf(char **strp, const char *fmt, va_list list) +{ + int ret; + + if ((ret = vasprintf(strp, fmt, list)) == -1) + *strp = NULL; + + return ret; +} + +/** * virAsprintf * * like glibc's_asprintf but makes sure *strp == NULL on failure @@ -2208,10 +2226,7 @@ virAsprintf(char **strp, const char *fmt, ...) int ret; va_start(ap, fmt); - - if ((ret = vasprintf(strp, fmt, ap)) == -1) - *strp = NULL; - + ret = virVasprintf(strp, fmt, ap); va_end(ap); return ret; } diff --git a/src/util/util.h b/src/util/util.h index a240d87..deaf8bb 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -31,12 +31,13 @@ # include # include # include +# include # ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) # endif -int saferead(int fd, void *buf, size_t count) ATTRIBUTE_RETURN_CHECK; +ssize_t saferead(int fd, void *buf, size_t count) ATTRIBUTE_RETURN_CHECK; ssize_t safewrite(int fd, const void *buf, size_t count) ATTRIBUTE_RETURN_CHECK; int safezero(int fd, int flags, off_t offset, off_t len) @@ -202,7 +203,10 @@ int virMacAddrCompare (const char *mac1, const char *mac2); void virSkipSpaces(const char **str); int virParseNumber(const char **str); int virParseVersionString(const char *str, unsigned long *version); -int virAsprintf(char **strp, const char *fmt, ...) ATTRIBUTE_FMT_PRINTF(2, 3); +int virAsprintf(char **strp, const char *fmt, ...) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3); +int virVasprintf(char **strp, const char *fmt, va_list list) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 0); char *virStrncpy(char *dest, const char *src, size_t n, size_t destbytes) ATTRIBUTE_RETURN_CHECK; char *virStrcpy(char *dest, const char *src, size_t destbytes) diff --git a/src/util/virtaudit.c b/src/util/virtaudit.c index b630fce..e6bd07f 100644 --- a/src/util/virtaudit.c +++ b/src/util/virtaudit.c @@ -94,7 +94,7 @@ void virAuditSend(const char *file ATTRIBUTE_UNUSED, const char *func, #endif va_start(args, fmt); - if (vasprintf(&str, fmt, args) < 0) { + if (virVasprintf(&str, fmt, args) < 0) { VIR_WARN0("Out of memory while formatting audit message"); str = NULL; } diff --git a/tests/commanddata/test16.log b/tests/commanddata/test16.log new file mode 100644 index 0000000..2433a4d --- /dev/null +++ b/tests/commanddata/test16.log @@ -0,0 +1 @@ +A=B /bin/true C diff --git a/tests/commandhelper.c b/tests/commandhelper.c index dc8998f..2ee9153 100644 --- a/tests/commandhelper.c +++ b/tests/commandhelper.c @@ -29,6 +29,7 @@ #include "internal.h" #include "util.h" #include "memory.h" +#include "files.h" static int envsort(const void *a, const void *b) { @@ -102,7 +103,7 @@ int main(int argc, char **argv) { strcpy(cwd, ".../commanddata"); fprintf(log, "CWD:%s\n", cwd); - fclose(log); + VIR_FORCE_FCLOSE(log); char buf[1024]; ssize_t got; diff --git a/tests/commandtest.c b/tests/commandtest.c index 5479bfc..46d6ae5 100644 --- a/tests/commandtest.c +++ b/tests/commandtest.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "testutils.h" #include "internal.h" @@ -32,6 +34,7 @@ #include "util.h" #include "memory.h" #include "command.h" +#include "files.h" #ifndef __linux__ @@ -166,10 +169,9 @@ static int test3(const void *unused ATTRIBUTE_UNUSED) { int newfd1 = dup(STDERR_FILENO); int newfd2 = dup(STDERR_FILENO); int newfd3 = dup(STDERR_FILENO); - close(newfd2); virCommandPreserveFD(cmd, newfd1); - virCommandPreserveFD(cmd, newfd3); + virCommandTransferFD(cmd, newfd3); if (virCommandRun(cmd, NULL) < 0) { virErrorPtr err = virGetLastError(); @@ -177,7 +179,16 @@ static int test3(const void *unused ATTRIBUTE_UNUSED) { return -1; } + if (fcntl(newfd1, F_GETFL) < 0 || + fcntl(newfd2, F_GETFL) < 0 || + fcntl(newfd3, F_GETFL) >= 0) { + puts("fds in wrong state"); + return -1; + } + virCommandFree(cmd); + VIR_FORCE_CLOSE(newfd1); + VIR_FORCE_CLOSE(newfd2); return checkoutput("test3"); } @@ -501,6 +512,52 @@ static int test15(const void *unused ATTRIBUTE_UNUSED) { return checkoutput("test15"); } +/* + * Don't run program; rather, log what would be run. + */ +static int test16(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew("/bin/true"); + char *outactual = NULL; + const char *outexpect = "A=B /bin/true C"; + int ret = -1; + int fd = -1; + + virCommandAddEnvPair(cmd, "A", "B"); + virCommandAddArg(cmd, "C"); + + if ((outactual = virCommandToString(cmd)) == NULL) { + virErrorPtr err = virGetLastError(); + printf("Cannot convert to string: %s\n", err->message); + return -1; + } + if ((fd = open(abs_builddir "/commandhelper.log", + O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) { + printf("Cannot open log file: %s\n", strerror (errno)); + goto cleanup; + } + virCommandWriteArgLog(cmd, fd); + if (VIR_CLOSE(fd) < 0) { + printf("Cannot close log file: %s\n", strerror (errno)); + goto cleanup; + } + + virCommandFree(cmd); + + if (checkoutput("test16") < 0) + goto cleanup; + + if (!STREQ(outactual, outexpect)) { + virtTestDifference(stderr, outactual, outexpect); + goto cleanup; + } + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + VIR_FREE(outactual); + return ret; +} + static int mymain(int argc, char **argv) { @@ -563,6 +620,7 @@ mymain(int argc, char **argv) DO_TEST(test13); DO_TEST(test14); DO_TEST(test15); + DO_TEST(test16); return(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index b149ef4..07dde54 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -25,28 +25,30 @@ static struct qemud_driver driver; # define MAX_FILE 4096 static int testCompareXMLToArgvFiles(const char *xml, - const char *cmd, + const char *cmdline, unsigned long long extraFlags, const char *migrateFrom, bool expectError) { char argvData[MAX_FILE]; char *expectargv = &(argvData[0]); + int len; char *actualargv = NULL; - const char **argv = NULL; - const char **qenv = NULL; - const char **tmp = NULL; - int ret = -1, len; + int ret = -1; unsigned long long flags; virDomainDefPtr vmdef = NULL; virDomainChrDef monitor_chr; virConnectPtr conn; char *log = NULL; + virCommandPtr cmd = NULL; if (!(conn = virGetConnect())) goto fail; - if (virtTestLoadFile(cmd, &expectargv, MAX_FILE) < 0) + len = virtTestLoadFile(cmdline, &expectargv, MAX_FILE); + if (len < 0) goto fail; + if (len && expectargv[len - 1] == '\n') + expectargv[len - 1] = '\0'; if (!(vmdef = virDomainDefParseFile(driver.caps, xml, VIR_DOMAIN_XML_INACTIVE))) @@ -85,10 +87,9 @@ static int testCompareXMLToArgvFiles(const char *xml, free(virtTestLogContentAndReset()); - if (qemudBuildCommandLine(conn, &driver, - vmdef, &monitor_chr, 0, flags, - &argv, &qenv, - NULL, NULL, migrateFrom, NULL) < 0) + if (!(cmd = qemudBuildCommandLine(conn, &driver, + vmdef, &monitor_chr, false, flags, + migrateFrom, NULL))) goto fail; if ((log = virtTestLogContentAndReset()) == NULL) @@ -105,36 +106,8 @@ static int testCompareXMLToArgvFiles(const char *xml, virResetLastError(); } - len = 1; /* for trailing newline */ - tmp = qenv; - while (*tmp) { - len += strlen(*tmp) + 1; - tmp++; - } - - tmp = argv; - while (*tmp) { - len += strlen(*tmp) + 1; - tmp++; - } - if ((actualargv = malloc(sizeof(*actualargv)*len)) == NULL) + if (!(actualargv = virCommandToString(cmd))) goto fail; - actualargv[0] = '\0'; - tmp = qenv; - while (*tmp) { - if (actualargv[0]) - strcat(actualargv, " "); - strcat(actualargv, *tmp); - tmp++; - } - tmp = argv; - while (*tmp) { - if (actualargv[0]) - strcat(actualargv, " "); - strcat(actualargv, *tmp); - tmp++; - } - strcat(actualargv, "\n"); if (STRNEQ(expectargv, actualargv)) { virtTestDifference(stderr, expectargv, actualargv); @@ -146,22 +119,7 @@ static int testCompareXMLToArgvFiles(const char *xml, fail: free(log); free(actualargv); - if (argv) { - tmp = argv; - while (*tmp) { - free(*(char**)tmp); - tmp++; - } - free(argv); - } - if (qenv) { - tmp = qenv; - while (*tmp) { - free(*(char**)tmp); - tmp++; - } - free(qenv); - } + virCommandFree(cmd); virDomainDefFree(vmdef); virUnrefConnect(conn); return ret;