From: "Daniel P. Berrange" <berrange(a)redhat.com>
---
cfg.mk | 4 +-
daemon/remote.c | 2 +-
po/POTFILES.in | 2 +-
src/Makefile.am | 2 +-
src/fdstream.h | 2 +-
src/libvirt.c | 2 +-
src/libxl/libxl_driver.c | 2 +-
src/lxc/lxc_container.c | 2 +-
src/lxc/lxc_controller.c | 2 +-
src/lxc/lxc_process.c | 2 +-
src/network/bridge_driver.c | 2 +-
src/network/bridge_driver.h | 2 +-
src/nwfilter/nwfilter_ebiptables_driver.c | 2 +-
src/openvz/openvz_conf.c | 2 +-
src/openvz/openvz_driver.c | 2 +-
src/openvz/openvz_util.c | 2 +-
src/parallels/parallels_driver.c | 2 +-
src/parallels/parallels_utils.c | 2 +-
src/qemu/qemu_capabilities.c | 2 +-
src/qemu/qemu_capabilities.h | 2 +-
src/qemu/qemu_command.h | 2 +-
src/qemu/qemu_conf.h | 2 +-
src/remote/remote_driver.c | 2 +-
src/rpc/virnetsocket.h | 2 +-
src/security/security_apparmor.c | 2 +-
src/security/virt-aa-helper.c | 2 +-
src/storage/storage_backend.h | 2 +-
src/storage/storage_backend_disk.c | 2 +-
src/storage/storage_backend_fs.c | 2 +-
src/storage/storage_backend_iscsi.c | 2 +-
src/storage/storage_backend_logical.c | 2 +-
src/storage/storage_backend_scsi.c | 2 +-
src/storage/storage_backend_sheepdog.c | 2 +-
src/uml/uml_conf.c | 2 +-
src/uml/uml_conf.h | 2 +-
src/util/command.c | 2523 -----------------------------
src/util/command.h | 166 --
src/util/dnsmasq.c | 2 +-
src/util/ebtables.c | 2 +-
src/util/hooks.c | 2 +-
src/util/iptables.c | 2 +-
src/util/pci.c | 2 +-
src/util/storage_file.c | 2 +-
src/util/sysinfo.c | 2 +-
src/util/util.c | 2 +-
src/util/vircommand.c | 2523 +++++++++++++++++++++++++++++
src/util/vircommand.h | 166 ++
src/util/virfile.c | 2 +-
src/util/virnetdev.c | 2 +-
src/util/virnetdevbandwidth.c | 2 +-
src/util/virnetdevopenvswitch.c | 2 +-
src/util/virnetdevveth.c | 2 +-
src/util/virnodesuspend.c | 2 +-
src/vmware/vmware_conf.c | 2 +-
src/vmware/vmware_driver.c | 2 +-
src/xen/xen_driver.c | 2 +-
tests/commandtest.c | 2 +-
tests/networkxml2conftest.c | 2 +-
tests/reconnect.c | 2 +-
tests/statstest.c | 2 +-
tests/testutils.c | 2 +-
tests/virnettlscontexttest.c | 2 +-
tools/virsh.c | 2 +-
63 files changed, 2749 insertions(+), 2749 deletions(-)
delete mode 100644 src/util/command.c
delete mode 100644 src/util/command.h
create mode 100644 src/util/vircommand.c
create mode 100644 src/util/vircommand.h
diff --git a/cfg.mk b/cfg.mk
index 1fe007e..9fbf799 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -745,7 +745,7 @@ $(srcdir)/src/remote/remote_client_bodies.h:
$(srcdir)/src/remote/remote_protoco
# List all syntax-check exemptions:
exclude_file_name_regexp--sc_avoid_strcase = ^tools/virsh\.h$$
-_src1=libvirt|fdstream|qemu/qemu_monitor|util/(command|util)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon
+_src1=libvirt|fdstream|qemu/qemu_monitor|util/(vircommand|util)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon
exclude_file_name_regexp--sc_avoid_write = \
^(src/($(_src1))|daemon/libvirtd|tools/console|tests/(shunload|virnettlscontext)test)\.c$$
@@ -778,7 +778,7 @@ exclude_file_name_regexp--sc_prohibit_close = \
exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \
(^tests/(qemuhelp|nodeinfo)data/|\.(gif|ico|png|diff)$$)
-_src2=src/(util/command|libvirt|lxc/lxc_controller|locking/lock_daemon)
+_src2=src/(util/vircommand|libvirt|lxc/lxc_controller|locking/lock_daemon)
exclude_file_name_regexp--sc_prohibit_fork_wrappers = \
(^($(_src2)|tests/testutils|daemon/libvirtd)\.c$$)
diff --git a/daemon/remote.c b/daemon/remote.c
index 41b8ea8..1746280 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -39,7 +39,7 @@
#include "stream.h"
#include "uuid.h"
#include "libvirt/libvirt-qemu.h"
-#include "command.h"
+#include "vircommand.h"
#include "intprops.h"
#include "virnetserverservice.h"
#include "virnetserver.h"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 19ed187..843db7c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -137,7 +137,6 @@ src/storage/storage_driver.c
src/test/test_driver.c
src/uml/uml_conf.c
src/uml/uml_driver.c
-src/util/command.c
src/util/conf.c
src/util/dnsmasq.c
src/util/event_poll.c
@@ -157,6 +156,7 @@ src/util/viraudit.c
src/util/virauth.c
src/util/virauthconfig.c
src/util/vircgroup.c
+src/util/vircommand.c
src/util/virdbus.c
src/util/virfile.c
src/util/virhash.c
diff --git a/src/Makefile.am b/src/Makefile.am
index fd21aa6..7e16d68 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -53,7 +53,6 @@ augeastest_DATA =
# These files are not related to driver APIs. Simply generic
# helper APIs for various purposes
UTIL_SOURCES = \
- util/command.c util/command.h \
util/conf.c util/conf.h \
util/event.c util/event.h \
util/event_poll.c util/event_poll.h \
@@ -83,6 +82,7 @@ UTIL_SOURCES = \
util/virauthconfig.c util/virauthconfig.h \
util/virbitmap.c util/virbitmap.h \
util/virbuffer.c util/virbuffer.h \
+ util/vircommand.c util/vircommand.h \
util/virfile.c util/virfile.h \
util/virnodesuspend.c util/virnodesuspend.h \
util/virobject.c util/virobject.h \
diff --git a/src/fdstream.h b/src/fdstream.h
index 65457d8..d6f5a7a 100644
--- a/src/fdstream.h
+++ b/src/fdstream.h
@@ -24,7 +24,7 @@
# define __VIR_FDSTREAM_H_
# include "internal.h"
-# include "command.h"
+# include "vircommand.h"
/* internal callback, the generic one is used up by daemon stream driver */
/* the close callback is called with fdstream private data locked */
diff --git a/src/libvirt.c b/src/libvirt.c
index 4215971..e28ed05 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -56,7 +56,7 @@
#include "intprops.h"
#include "conf.h"
#include "rpc/virnettlscontext.h"
-#include "command.h"
+#include "vircommand.h"
#include "virrandom.h"
#include "viruri.h"
#include "threads.h"
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index 302f81c..1423073 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -39,7 +39,7 @@
#include "virfile.h"
#include "memory.h"
#include "uuid.h"
-#include "command.h"
+#include "vircommand.h"
#include "libxl.h"
#include "libxl_driver.h"
#include "libxl_conf.h"
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index 3014564..33ebf1f 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -61,7 +61,7 @@
#include "virnetdevveth.h"
#include "uuid.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "virnetdev.h"
#include "virprocess.h"
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 5510b9a..c9cac5d 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -67,7 +67,7 @@
#include "util.h"
#include "virfile.h"
#include "virpidfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "processinfo.h"
#include "nodeinfo.h"
#include "virrandom.h"
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 3e7fcb8..7f66dc7 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -42,7 +42,7 @@
#include "domain_audit.h"
#include "virterror_internal.h"
#include "logging.h"
-#include "command.h"
+#include "vircommand.h"
#include "hooks.h"
#define VIR_FROM_THIS VIR_FROM_LXC
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index c65f0bb..319ff8c 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -53,7 +53,7 @@
#include "virbuffer.h"
#include "virpidfile.h"
#include "util.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "uuid.h"
#include "iptables.h"
diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h
index 43fefc0..fea27e0 100644
--- a/src/network/bridge_driver.h
+++ b/src/network/bridge_driver.h
@@ -30,7 +30,7 @@
# include "internal.h"
# include "network_conf.h"
# include "domain_conf.h"
-# include "command.h"
+# include "vircommand.h"
# include "dnsmasq.h"
int networkRegister(void);
diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c
b/src/nwfilter/nwfilter_ebiptables_driver.c
index 5cfc036..6966acf 100644
--- a/src/nwfilter/nwfilter_ebiptables_driver.c
+++ b/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -40,7 +40,7 @@
#include "nwfilter_gentech_driver.h"
#include "nwfilter_ebiptables_driver.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "configmake.h"
#include "intprops.h"
diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c
index 3f89db3..c9189ef 100644
--- a/src/openvz/openvz_conf.c
+++ b/src/openvz/openvz_conf.c
@@ -52,7 +52,7 @@
#include "util.h"
#include "nodeinfo.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#define VIR_FROM_THIS VIR_FROM_OPENVZ
diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c
index 1b213c4..75d52e2 100644
--- a/src/openvz/openvz_driver.c
+++ b/src/openvz/openvz_driver.c
@@ -57,7 +57,7 @@
#include "virfile.h"
#include "virtypedparam.h"
#include "logging.h"
-#include "command.h"
+#include "vircommand.h"
#include "viruri.h"
#include "stats_linux.h"
diff --git a/src/openvz/openvz_util.c b/src/openvz/openvz_util.c
index 111045f..4163e19 100644
--- a/src/openvz/openvz_util.c
+++ b/src/openvz/openvz_util.c
@@ -26,7 +26,7 @@
#include "internal.h"
#include "virterror_internal.h"
-#include "command.h"
+#include "vircommand.h"
#include "datatypes.h"
#include "memory.h"
diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c
index 60507e7..648cb48 100644
--- a/src/parallels/parallels_driver.c
+++ b/src/parallels/parallels_driver.c
@@ -46,7 +46,7 @@
#include "memory.h"
#include "util.h"
#include "logging.h"
-#include "command.h"
+#include "vircommand.h"
#include "configmake.h"
#include "storage_file.h"
#include "nodeinfo.h"
diff --git a/src/parallels/parallels_utils.c b/src/parallels/parallels_utils.c
index 521fd97..e47ff76 100644
--- a/src/parallels/parallels_utils.c
+++ b/src/parallels/parallels_utils.c
@@ -24,7 +24,7 @@
#include <stdarg.h>
-#include "command.h"
+#include "vircommand.h"
#include "virterror_internal.h"
#include "memory.h"
#include "json.h"
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 45962b0..f68e081 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -34,7 +34,7 @@
#include "nodeinfo.h"
#include "cpu/cpu.h"
#include "domain_conf.h"
-#include "command.h"
+#include "vircommand.h"
#include "virbitmap.h"
#include "virnodesuspend.h"
#include "qemu_monitor.h"
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index bf4eef8..cd7ee92 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -26,7 +26,7 @@
# include "virobject.h"
# include "capabilities.h"
-# include "command.h"
+# include "vircommand.h"
# include "virobject.h"
# include "qemu_monitor.h"
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 0dde4be..b3ac4a6 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -25,7 +25,7 @@
# define __QEMU_COMMAND_H__
# include "domain_conf.h"
-# include "command.h"
+# include "vircommand.h"
# include "capabilities.h"
# include "qemu_conf.h"
# include "qemu_domain.h"
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index bcf21c3..eafaf9f 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -40,7 +40,7 @@
# include "cpu_conf.h"
# include "driver.h"
# include "virbitmap.h"
-# include "command.h"
+# include "vircommand.h"
# include "threadpool.h"
# include "locking/lock_manager.h"
# include "qemu_capabilities.h"
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index f32e88e..8b77e7d 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -41,7 +41,7 @@
#include "memory.h"
#include "util.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "intprops.h"
#include "virtypedparam.h"
#include "viruri.h"
diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h
index e024640..fcd54dd 100644
--- a/src/rpc/virnetsocket.h
+++ b/src/rpc/virnetsocket.h
@@ -25,7 +25,7 @@
# define __VIR_NET_SOCKET_H__
# include "virsocketaddr.h"
-# include "command.h"
+# include "vircommand.h"
# include "virnettlscontext.h"
# include "virobject.h"
# ifdef HAVE_SASL
diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c
index b0cdb65..ff8aea1 100644
--- a/src/security/security_apparmor.c
+++ b/src/security/security_apparmor.c
@@ -47,7 +47,7 @@
#include "hostusb.h"
#include "virfile.h"
#include "configmake.h"
-#include "command.h"
+#include "vircommand.h"
#include "logging.h"
#define VIR_FROM_THIS VIR_FROM_SECURITY
diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c
index 78ebae3..bfd6305 100644
--- a/src/security/virt-aa-helper.c
+++ b/src/security/virt-aa-helper.c
@@ -43,7 +43,7 @@
#include "virbuffer.h"
#include "util.h"
#include "memory.h"
-#include "command.h"
+#include "vircommand.h"
#include "security_driver.h"
#include "security_apparmor.h"
diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h
index c991015..56b6797 100644
--- a/src/storage/storage_backend.h
+++ b/src/storage/storage_backend.h
@@ -26,7 +26,7 @@
# include "internal.h"
# include "storage_conf.h"
-# include "command.h"
+# include "vircommand.h"
typedef char * (*virStorageBackendFindPoolSources)(virConnectPtr conn, const char
*srcSpec, unsigned int flags);
typedef int (*virStorageBackendCheckPool)(virConnectPtr conn, virStoragePoolObjPtr pool,
bool *active);
diff --git a/src/storage/storage_backend_disk.c b/src/storage/storage_backend_disk.c
index 06b5909..c6aa407 100644
--- a/src/storage/storage_backend_disk.c
+++ b/src/storage/storage_backend_disk.c
@@ -31,7 +31,7 @@
#include "storage_backend_disk.h"
#include "util.h"
#include "memory.h"
-#include "command.h"
+#include "vircommand.h"
#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index fcc46b7..cdf93af 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -45,7 +45,7 @@
#include "storage_backend_fs.h"
#include "storage_conf.h"
#include "storage_file.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "xml.h"
#include "virfile.h"
diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c
index b080250..c468b1b 100644
--- a/src/storage/storage_backend_iscsi.c
+++ b/src/storage/storage_backend_iscsi.c
@@ -41,7 +41,7 @@
#include "memory.h"
#include "logging.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "virrandom.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
diff --git a/src/storage/storage_backend_logical.c
b/src/storage/storage_backend_logical.c
index fd5cbd1..53e6c61 100644
--- a/src/storage/storage_backend_logical.c
+++ b/src/storage/storage_backend_logical.c
@@ -34,7 +34,7 @@
#include "virterror_internal.h"
#include "storage_backend_logical.h"
#include "storage_conf.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "logging.h"
#include "virfile.h"
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c
index 4e832eb..6515e57 100644
--- a/src/storage/storage_backend_scsi.c
+++ b/src/storage/storage_backend_scsi.c
@@ -33,7 +33,7 @@
#include "memory.h"
#include "logging.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
diff --git a/src/storage/storage_backend_sheepdog.c
b/src/storage/storage_backend_sheepdog.c
index 66d8fb4..ecca7a8 100644
--- a/src/storage/storage_backend_sheepdog.c
+++ b/src/storage/storage_backend_sheepdog.c
@@ -29,7 +29,7 @@
#include "virterror_internal.h"
#include "storage_backend_sheepdog.h"
#include "storage_conf.h"
-#include "util/command.h"
+#include "vircommand.h"
#include "util.h"
#include "memory.h"
#include "logging.h"
diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c
index 11c915e..6aec8fc 100644
--- a/src/uml/uml_conf.c
+++ b/src/uml/uml_conf.c
@@ -45,7 +45,7 @@
#include "logging.h"
#include "domain_nwfilter.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "virnetdevtap.h"
#include "virnodesuspend.h"
diff --git a/src/uml/uml_conf.h b/src/uml/uml_conf.h
index 9bddedc..09a0305 100644
--- a/src/uml/uml_conf.h
+++ b/src/uml/uml_conf.h
@@ -31,7 +31,7 @@
# include "domain_event.h"
# include "virterror_internal.h"
# include "threads.h"
-# include "command.h"
+# include "vircommand.h"
# include "virhash.h"
# define umlDebug(fmt, ...) do {} while(0)
diff --git a/src/util/command.c b/src/util/command.c
deleted file mode 100644
index fbd9ff8..0000000
--- a/src/util/command.c
+++ /dev/null
@@ -1,2523 +0,0 @@
-/*
- * command.c: Child command execution
- *
- * Copyright (C) 2010-2012 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see
- * <
http://www.gnu.org/licenses/>.
- *
- */
-
-#include <config.h>
-
-#include <poll.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-
-#if HAVE_CAPNG
-# include <cap-ng.h>
-#endif
-
-#include "command.h"
-#include "memory.h"
-#include "virterror_internal.h"
-#include "util.h"
-#include "logging.h"
-#include "virfile.h"
-#include "virpidfile.h"
-#include "virprocess.h"
-#include "virbuffer.h"
-
-#define VIR_FROM_THIS VIR_FROM_NONE
-
-/* Flags for virExecWithHook */
-enum {
- VIR_EXEC_NONE = 0,
- VIR_EXEC_NONBLOCK = (1 << 0),
- VIR_EXEC_DAEMON = (1 << 1),
- VIR_EXEC_CLEAR_CAPS = (1 << 2),
- VIR_EXEC_RUN_SYNC = (1 << 3),
-};
-
-struct _virCommand {
- int has_error; /* ENOMEM on allocation failure, -1 for anything else. */
-
- char **args;
- size_t nargs;
- size_t maxargs;
-
- char **env;
- size_t nenv;
- size_t maxenv;
-
- char *pwd;
-
- int *preserve; /* FDs to pass to child. */
- int preserve_size;
- int *transfer; /* FDs to close in parent. */
- int transfer_size;
-
- unsigned int flags;
-
- char *inbuf;
- char **outbuf;
- char **errbuf;
-
- int infd;
- int outfd;
- int errfd;
- int *outfdptr;
- int *errfdptr;
-
- bool handshake;
- int handshakeWait[2];
- int handshakeNotify[2];
-
- virExecHook hook;
- void *opaque;
-
- pid_t pid;
- char *pidfile;
- bool reap;
-
- unsigned long long capabilities;
-};
-
-/*
- * virCommandFDIsSet:
- * @fd: FD to test
- * @set: the set
- * @set_size: actual size of @set
- *
- * Check if FD is already in @set or not.
- *
- * Returns true if @set contains @fd,
- * false otherwise.
- */
-static bool
-virCommandFDIsSet(int fd,
- const int *set,
- int set_size)
-{
- int i = 0;
-
- while (i < set_size)
- if (set[i++] == fd)
- return true;
-
- return false;
-}
-
-/*
- * virCommandFDSet:
- * @fd: FD to be put into @set
- * @set: the set
- * @set_size: actual size of @set
- *
- * This is practically generalized implementation
- * of FD_SET() as we do not want to be limited
- * by FD_SETSIZE.
- *
- * Returns: 0 on success,
- * -1 on usage error,
- * ENOMEM on OOM
- */
-static int
-virCommandFDSet(int fd,
- int **set,
- int *set_size)
-{
- if (fd < 0 || !set || !set_size)
- return -1;
-
- if (virCommandFDIsSet(fd, *set, *set_size))
- return 0;
-
- if (VIR_REALLOC_N(*set, *set_size + 1) < 0) {
- return ENOMEM;
- }
-
- (*set)[*set_size] = fd;
- (*set_size)++;
-
- return 0;
-}
-
-#ifndef WIN32
-
-static int virClearCapabilities(void) ATTRIBUTE_UNUSED;
-
-# if HAVE_CAPNG
-static int virClearCapabilities(void)
-{
- int ret;
-
- capng_clear(CAPNG_SELECT_BOTH);
-
- if ((ret = capng_apply(CAPNG_SELECT_BOTH)) < 0) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot clear process capabilities %d"), ret);
- return -1;
- }
-
- return 0;
-}
-
-/**
- * virSetCapabilities:
- * @capabilities - capability flag to set.
- * In case of 0, this function is identical to
- * virClearCapabilities()
- *
- */
-static int virSetCapabilities(unsigned long long capabilities)
-{
- int ret, i;
-
- capng_clear(CAPNG_SELECT_BOTH);
-
- for (i = 0; i <= CAP_LAST_CAP; i++) {
- if (capabilities & (1ULL << i))
- capng_update(CAPNG_ADD, CAPNG_BOUNDING_SET, i);
- }
-
- if ((ret = capng_apply(CAPNG_SELECT_BOTH)) < 0) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot apply process capabilities %d"), ret);
- return -1;
- }
-
- return 0;
-}
-# else
-static int virClearCapabilities(void)
-{
-// VIR_WARN("libcap-ng support not compiled in, unable to clear "
-// "capabilities");
- return 0;
-}
-
-static int
-virSetCapabilities(unsigned long long capabilities ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-# endif
-
-/**
- * virFork:
- * @pid - a pointer to a pid_t that will receive the return value from
- * fork()
- *
- * fork a new process while avoiding various race/deadlock conditions
- *
- * on return from virFork(), if *pid < 0, the fork failed and there is
- * no new process. Otherwise, just like fork(), if *pid == 0, it is the
- * child process returning, and if *pid > 0, it is the parent.
- *
- * Even if *pid >= 0, if the return value from virFork() is < 0, it
- * indicates a failure that occurred in the parent or child process
- * after the fork. In this case, the child process should call
- * _exit(EXIT_FAILURE) after doing any additional error reporting.
- */
-int
-virFork(pid_t *pid)
-{
- sigset_t oldmask, newmask;
- struct sigaction sig_action;
- int saved_errno, ret = -1;
-
- *pid = -1;
-
- /*
- * Need to block signals now, so that child process can safely
- * kill off caller's signal handlers without a race.
- */
- sigfillset(&newmask);
- if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
- saved_errno = errno;
- virReportSystemError(errno,
- "%s", _("cannot block signals"));
- goto cleanup;
- }
-
- /* Ensure we hold the logging lock, to protect child processes
- * from deadlocking on another thread's inherited mutex state */
- virLogLock();
-
- *pid = fork();
- saved_errno = errno; /* save for caller */
-
- /* Unlock for both parent and child process */
- virLogUnlock();
-
- if (*pid < 0) {
- /* attempt to restore signal mask, but ignore failure, to
- avoid obscuring the fork failure */
- ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL));
- virReportSystemError(saved_errno,
- "%s", _("cannot fork child process"));
- goto cleanup;
- }
-
- if (*pid) {
-
- /* parent process */
-
- /* Restore our original signal mask now that the child is
- safely running */
- if (pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0) {
- saved_errno = errno; /* save for caller */
- virReportSystemError(errno, "%s", _("cannot unblock
signals"));
- goto cleanup;
- }
- ret = 0;
-
- } else {
-
- /* child process */
-
- int logprio;
- int i;
-
- /* Remove any error callback so errors in child now
- get sent to stderr where they stand a fighting chance
- of being seen / logged */
- virSetErrorFunc(NULL, NULL);
- virSetErrorLogPriorityFunc(NULL);
-
- /* Make sure any hook logging is sent to stderr, since child
- * process may close the logfile FDs */
- logprio = virLogGetDefaultPriority();
- virLogReset();
- virLogSetDefaultPriority(logprio);
-
- /* Clear out all signal handlers from parent so nothing
- unexpected can happen in our child once we unblock
- signals */
- sig_action.sa_handler = SIG_DFL;
- sig_action.sa_flags = 0;
- sigemptyset(&sig_action.sa_mask);
-
- for (i = 1; i < NSIG; i++) {
- /* Only possible errors are EFAULT or EINVAL
- The former wont happen, the latter we
- expect, so no need to check return value */
-
- sigaction(i, &sig_action, NULL);
- }
-
- /* Unmask all signals in child, since we've no idea
- what the caller's done with their signal mask
- and don't want to propagate that to children */
- sigemptyset(&newmask);
- if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) {
- saved_errno = errno; /* save for caller */
- virReportSystemError(errno, "%s", _("cannot unblock
signals"));
- goto cleanup;
- }
- ret = 0;
- }
-
-cleanup:
- if (ret < 0)
- errno = saved_errno;
- return ret;
-}
-
-/*
- * Ensure that *null is an fd visiting /dev/null. Return 0 on
- * success, -1 on failure. Allows for lazy opening of shared
- * /dev/null fd only as required.
- */
-static int
-getDevNull(int *null)
-{
- if (*null == -1 && (*null = open("/dev/null", O_RDWR|O_CLOEXEC))
< 0) {
- virReportSystemError(errno,
- _("cannot open %s"),
- "/dev/null");
- return -1;
- }
- return 0;
-}
-
-/* Ensure that STD is an inheritable copy of FD. Return 0 on success,
- * -1 on failure. */
-static int
-prepareStdFd(int fd, int std)
-{
- if (fd == std)
- return virSetInherit(fd, true);
- if (dup2(fd, std) != std)
- return -1;
- return 0;
-}
-
-/*
- * @argv argv to exec
- * @envp optional environment to use for exec
- * @keepfd options fd_ret to keep open for child process
- * @retpid optional pointer to store child process pid
- * @infd optional file descriptor to use as child input, otherwise /dev/null
- * @outfd optional pointer to communicate output fd behavior
- * outfd == NULL : Use /dev/null
- * *outfd == -1 : Use a new fd
- * *outfd != -1 : Use *outfd
- * @errfd optional pointer to communcate error fd behavior. See outfd
- * @flags possible combination of the following:
- * VIR_EXEC_NONE : Default function behavior
- * VIR_EXEC_NONBLOCK : Set child process output fd's as non-blocking
- * VIR_EXEC_DAEMON : Daemonize the child process
- * @hook optional virExecHook function to call prior to exec
- * @data data to pass to the hook function
- * @pidfile path to use as pidfile for daemonized process (needs DAEMON flag)
- * @capabilities capabilities to keep
- */
-static int
-virExecWithHook(const char *const*argv,
- const char *const*envp,
- const int *keepfd,
- int keepfd_size,
- pid_t *retpid,
- int infd, int *outfd, int *errfd,
- unsigned int flags,
- virExecHook hook,
- void *data,
- char *pidfile,
- unsigned long long capabilities)
-{
- pid_t pid;
- int null = -1, i, openmax;
- int pipeout[2] = {-1,-1};
- int pipeerr[2] = {-1,-1};
- int childout = -1;
- int childerr = -1;
- int tmpfd;
- const char *binary = NULL;
- int forkRet;
-
- if (argv[0][0] != '/') {
- if (!(binary = virFindFileInPath(argv[0]))) {
- virReportSystemError(ENOENT,
- _("Cannot find '%s' in path"),
- argv[0]);
- return -1;
- }
- } else {
- binary = argv[0];
- }
-
- if (infd < 0) {
- if (getDevNull(&null) < 0)
- goto cleanup;
- infd = null;
- }
-
- if (outfd != NULL) {
- if (*outfd == -1) {
- if (pipe2(pipeout, O_CLOEXEC) < 0) {
- virReportSystemError(errno,
- "%s", _("cannot create pipe"));
- goto cleanup;
- }
-
- if ((flags & VIR_EXEC_NONBLOCK) &&
- virSetNonBlock(pipeout[0]) == -1) {
- virReportSystemError(errno,
- "%s", _("Failed to set non-blocking
file descriptor flag"));
- goto cleanup;
- }
-
- childout = pipeout[1];
- } else {
- childout = *outfd;
- }
- } else {
- if (getDevNull(&null) < 0)
- goto cleanup;
- childout = null;
- }
-
- if (errfd != NULL) {
- if (errfd == outfd) {
- childerr = childout;
- } else if (*errfd == -1) {
- if (pipe2(pipeerr, O_CLOEXEC) < 0) {
- virReportSystemError(errno,
- "%s", _("Failed to create
pipe"));
- goto cleanup;
- }
-
- if ((flags & VIR_EXEC_NONBLOCK) &&
- virSetNonBlock(pipeerr[0]) == -1) {
- virReportSystemError(errno,
- "%s", _("Failed to set non-blocking
file descriptor flag"));
- goto cleanup;
- }
-
- childerr = pipeerr[1];
- } else {
- childerr = *errfd;
- }
- } else {
- if (getDevNull(&null) < 0)
- goto cleanup;
- childerr = null;
- }
-
- forkRet = virFork(&pid);
-
- if (pid < 0) {
- goto cleanup;
- }
-
- if (pid) { /* parent */
- if (forkRet < 0) {
- goto cleanup;
- }
-
- VIR_FORCE_CLOSE(null);
- if (outfd && *outfd == -1) {
- VIR_FORCE_CLOSE(pipeout[1]);
- *outfd = pipeout[0];
- }
- if (errfd && *errfd == -1) {
- VIR_FORCE_CLOSE(pipeerr[1]);
- *errfd = pipeerr[0];
- }
-
- *retpid = pid;
-
- if (binary != argv[0])
- VIR_FREE(binary);
-
- return 0;
- }
-
- /* child */
-
- if (forkRet < 0) {
- /* The fork was successful, but after that there was an error
- * in the child (which was already logged).
- */
- goto fork_error;
- }
-
- openmax = sysconf(_SC_OPEN_MAX);
- for (i = 3; i < openmax; i++) {
- if (i == infd || i == childout || i == childerr)
- continue;
- if (!keepfd || !virCommandFDIsSet(i, keepfd, keepfd_size)) {
- tmpfd = i;
- VIR_MASS_CLOSE(tmpfd);
- } else if (virSetInherit(i, true) < 0) {
- virReportSystemError(errno, _("failed to preserve fd %d"), i);
- goto fork_error;
- }
- }
-
- if (prepareStdFd(infd, STDIN_FILENO) < 0) {
- virReportSystemError(errno,
- "%s", _("failed to setup stdin file
handle"));
- goto fork_error;
- }
- if (childout > 0 && prepareStdFd(childout, STDOUT_FILENO) < 0) {
- virReportSystemError(errno,
- "%s", _("failed to setup stdout file
handle"));
- goto fork_error;
- }
- if (childerr > 0 && prepareStdFd(childerr, STDERR_FILENO) < 0) {
- virReportSystemError(errno,
- "%s", _("failed to setup stderr file
handle"));
- goto fork_error;
- }
-
- if (infd != STDIN_FILENO && infd != null && infd != childerr
&&
- infd != childout)
- VIR_FORCE_CLOSE(infd);
- if (childout > STDERR_FILENO && childout != null && childout !=
childerr)
- VIR_FORCE_CLOSE(childout);
- if (childerr > STDERR_FILENO && childerr != null)
- VIR_FORCE_CLOSE(childerr);
- VIR_FORCE_CLOSE(null);
-
- /* Initialize full logging for a while */
- virLogSetFromEnv();
-
- /* Daemonize as late as possible, so the parent process can detect
- * the above errors with wait* */
- if (flags & VIR_EXEC_DAEMON) {
- if (setsid() < 0) {
- virReportSystemError(errno,
- "%s", _("cannot become session
leader"));
- goto fork_error;
- }
-
- if (chdir("/") < 0) {
- virReportSystemError(errno,
- "%s", _("cannot change to root
directory"));
- goto fork_error;
- }
-
- pid = fork();
- if (pid < 0) {
- virReportSystemError(errno,
- "%s", _("cannot fork child
process"));
- goto fork_error;
- }
-
- if (pid > 0) {
- if (pidfile && (virPidFileWritePath(pidfile,pid) < 0)) {
- kill(pid, SIGTERM);
- usleep(500*1000);
- kill(pid, SIGTERM);
- virReportSystemError(errno,
- _("could not write pidfile %s for %d"),
- pidfile, pid);
- goto fork_error;
- }
- _exit(0);
- }
- }
-
- if (hook) {
- /* virFork reset all signal handlers to the defaults.
- * This is good for the child process, but our hook
- * risks running something that generates SIGPIPE,
- * so we need to temporarily block that again
- */
- struct sigaction waxon, waxoff;
- memset(&waxoff, 0, sizeof(waxoff));
- waxoff.sa_handler = SIG_IGN;
- sigemptyset(&waxoff.sa_mask);
- memset(&waxon, 0, sizeof(waxon));
- if (sigaction(SIGPIPE, &waxoff, &waxon) < 0) {
- virReportSystemError(errno, "%s",
- _("Could not disable SIGPIPE"));
- goto fork_error;
- }
-
- if ((hook)(data) != 0) {
- VIR_DEBUG("Hook function failed.");
- goto fork_error;
- }
-
- if (sigaction(SIGPIPE, &waxon, NULL) < 0) {
- virReportSystemError(errno, "%s",
- _("Could not re-enable SIGPIPE"));
- goto fork_error;
- }
- }
-
- /* The steps above may need todo something privileged, so
- * we delay clearing capabilities until the last minute */
- if (capabilities || (flags & VIR_EXEC_CLEAR_CAPS))
- if (virSetCapabilities(capabilities) < 0)
- goto fork_error;
-
- /* Close logging again to ensure no FDs leak to child */
- virLogReset();
-
- if (envp)
- execve(binary, (char **) argv, (char**)envp);
- else
- execv(binary, (char **) argv);
-
- virReportSystemError(errno,
- _("cannot execute binary %s"),
- argv[0]);
-
- fork_error:
- virDispatchError(NULL);
- _exit(EXIT_FAILURE);
-
- cleanup:
- /* This is cleanup of parent process only - child
- should never jump here on error */
-
- if (binary != argv[0])
- VIR_FREE(binary);
-
- /* NB we don't virReportError() on any failures here
- because the code which jumped here already raised
- an error condition which we must not overwrite */
- VIR_FORCE_CLOSE(pipeerr[0]);
- VIR_FORCE_CLOSE(pipeerr[1]);
- VIR_FORCE_CLOSE(pipeout[0]);
- VIR_FORCE_CLOSE(pipeout[1]);
- VIR_FORCE_CLOSE(null);
- return -1;
-}
-
-/**
- * virRun:
- * @argv NULL terminated argv to run
- * @status optional variable to return exit status in
- *
- * Run a command without using the shell.
- *
- * If status is NULL, then return 0 if the command run and
- * exited with 0 status; Otherwise return -1
- *
- * If status is not-NULL, then return 0 if the command ran.
- * The status variable is filled with the command exit status
- * and should be checked by caller for success. Return -1
- * only if the command could not be run.
- */
-int
-virRun(const char *const*argv, int *status)
-{
- int ret;
- virCommandPtr cmd = virCommandNewArgs(argv);
-
- ret = virCommandRun(cmd, status);
- virCommandFree(cmd);
- return ret;
-}
-
-#else /* WIN32 */
-
-int
-virRun(const char *const *argv ATTRIBUTE_UNUSED,
- int *status)
-{
- if (status)
- *status = ENOTSUP;
- else
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("virRun is not implemented for
WIN32"));
- return -1;
-}
-
-static int
-virExecWithHook(const char *const*argv ATTRIBUTE_UNUSED,
- const char *const*envp ATTRIBUTE_UNUSED,
- const int *keepfd ATTRIBUTE_UNUSED,
- int keepfd_size ATTRIBUTE_UNUSED,
- pid_t *retpid ATTRIBUTE_UNUSED,
- int infd ATTRIBUTE_UNUSED,
- int *outfd ATTRIBUTE_UNUSED,
- int *errfd ATTRIBUTE_UNUSED,
- int flags_unused ATTRIBUTE_UNUSED,
- virExecHook hook ATTRIBUTE_UNUSED,
- void *data ATTRIBUTE_UNUSED,
- char *pidfile ATTRIBUTE_UNUSED,
- unsigned long long capabilities ATTRIBUTE_UNUSED)
-{
- /* XXX: Some day we can implement pieces of virCommand/virExec on
- * top of _spawn() or CreateProcess(), but we can't implement
- * everything, since mingw completely lacks fork(), so we cannot
- * run hook code in the child. */
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("virExec is not implemented for WIN32"));
- return -1;
-}
-
-int
-virFork(pid_t *pid)
-{
- *pid = -1;
- errno = ENOTSUP;
-
- return -1;
-}
-
-#endif /* WIN32 */
-
-
-/**
- * virCommandNew:
- * @binary: program to run
- *
- * Create a new command for named binary. If @binary is relative,
- * it will be found via a PATH search of the parent's PATH (and not
- * any altered PATH set by virCommandAddEnv* commands).
- */
-virCommandPtr
-virCommandNew(const char *binary)
-{
- const char *const args[] = { binary, NULL };
-
- return virCommandNewArgs(args);
-}
-
-/**
- * virCommandNewArgs:
- * @args: array of arguments
- *
- * Create a new command with a NULL terminated
- * set of args, taking binary from args[0]. More arguments can
- * be added later. @args[0] is handled like @binary of virCommandNew.
- */
-virCommandPtr
-virCommandNewArgs(const char *const*args)
-{
- virCommandPtr cmd;
-
- if (VIR_ALLOC(cmd) < 0)
- return NULL;
-
- cmd->handshakeWait[0] = -1;
- cmd->handshakeWait[1] = -1;
- cmd->handshakeNotify[0] = -1;
- cmd->handshakeNotify[1] = -1;
-
- cmd->infd = cmd->outfd = cmd->errfd = -1;
- cmd->pid = -1;
-
- virCommandAddArgSet(cmd, args);
-
- return cmd;
-}
-
-/**
- * virCommandNewArgList:
- * @binary: program to run
- * @...: additional arguments
- *
- * Create a new command with a NULL terminated
- * list of args, starting with the binary to run. More arguments can
- * be added later. @binary is handled as in virCommandNew.
- */
-virCommandPtr
-virCommandNewArgList(const char *binary, ...)
-{
- virCommandPtr cmd = virCommandNew(binary);
- va_list list;
- const char *arg;
-
- if (!cmd || cmd->has_error)
- return cmd;
-
- va_start(list, binary);
- while ((arg = va_arg(list, const char *)) != NULL)
- virCommandAddArg(cmd, arg);
- va_end(list);
- return cmd;
-}
-
-/**
- * virCommandNewVAList:
- * @binary: program to run
- * @va_list: additional arguments
- *
- * Create a new command with a NULL terminated
- * variable argument list. @binary is handled as in virCommandNew.
- */
-virCommandPtr
-virCommandNewVAList(const char *binary, va_list list)
-{
- virCommandPtr cmd = virCommandNew(binary);
- const char *arg;
-
- if (!cmd || cmd->has_error)
- return cmd;
-
- while ((arg = va_arg(list, const char *)) != NULL)
- virCommandAddArg(cmd, arg);
- return cmd;
-}
-
-
-/*
- * Preserve the specified file descriptor in the child, instead of
- * 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.
- * Returns true if a transferring caller should close FD now, and
- * false if the transfer is successfully recorded.
- */
-static bool
-virCommandKeepFD(virCommandPtr cmd, int fd, bool transfer)
-{
- int ret = 0;
-
- if (!cmd)
- return fd > STDERR_FILENO;
-
- if (fd <= STDERR_FILENO ||
- (ret = virCommandFDSet(fd, &cmd->preserve, &cmd->preserve_size))
||
- (transfer && (ret = virCommandFDSet(fd, &cmd->transfer,
- &cmd->transfer_size)))) {
- if (!cmd->has_error)
- cmd->has_error = ret ? ret : -1 ;
- VIR_DEBUG("cannot preserve %d", fd);
- return fd > STDERR_FILENO;
- }
-
- return false;
-}
-
-/**
- * virCommandPreserveFD:
- * @cmd: the command to modify
- * @fd: fd to mark for inheritance into child
- *
- * Preserve the specified file descriptor
- * in the child, instead of closing it on exec.
- * The parent is still responsible for managing fd.
- */
-void
-virCommandPreserveFD(virCommandPtr cmd, int fd)
-{
- virCommandKeepFD(cmd, fd, false);
-}
-
-/**
- * virCommandTransferFD:
- * @cmd: the command to modify
- * @fd: fd to reassign to the child
- *
- * Transfer the specified file descriptor
- * to the child, instead of closing it on exec.
- * The parent should no longer use fd, and the parent's copy will
- * be automatically closed no later than during Run/RunAsync/Free.
- */
-void
-virCommandTransferFD(virCommandPtr cmd, int fd)
-{
- if (virCommandKeepFD(cmd, fd, true))
- VIR_FORCE_CLOSE(fd);
-}
-
-
-/**
- * virCommandSetPidFile:
- * @cmd: the command to modify
- * @pidfile: filename to use
- *
- * Save the child PID in a pidfile. The pidfile will be populated
- * before the exec of the child.
- */
-void
-virCommandSetPidFile(virCommandPtr cmd, const char *pidfile)
-{
- if (!cmd || cmd->has_error)
- return;
-
- VIR_FREE(cmd->pidfile);
- if (!(cmd->pidfile = strdup(pidfile))) {
- cmd->has_error = ENOMEM;
- }
-}
-
-
-/**
- * virCommandClearCaps:
- * @cmd: the command to modify
- *
- * Remove all capabilities from the child, after any hooks have been run.
- */
-void
-virCommandClearCaps(virCommandPtr cmd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- cmd->flags |= VIR_EXEC_CLEAR_CAPS;
-}
-
-/**
- * virCommandAllowCap:
- * @cmd: the command to modify
- * @capability: what to allow
- *
- * Allow specific capabilities
- */
-void
-virCommandAllowCap(virCommandPtr cmd,
- int capability)
-{
- if (!cmd || cmd->has_error)
- return;
-
- cmd->capabilities |= (1ULL << capability);
-}
-
-
-
-/**
- * virCommandDaemonize:
- * @cmd: the command to modify
- *
- * Daemonize the child process. The child will have a current working
- * directory of /, and must be started with virCommandRun, which will
- * complete as soon as the daemon grandchild has started.
- */
-void
-virCommandDaemonize(virCommandPtr cmd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- cmd->flags |= VIR_EXEC_DAEMON;
-}
-
-/**
- * virCommandNonblockingFDs:
- * @cmd: the command to modify
- *
- * 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 cmd->env list. 'env' is a
- * string like "name=value". If the named environment variable is
- * already set, then it is replaced in the list.
- */
-static inline void
-virCommandAddEnv(virCommandPtr cmd, char *env)
-{
- size_t namelen;
- size_t i;
-
- /* Search for the name in the existing environment. */
- namelen = strcspn(env, "=");
- for (i = 0; i < cmd->nenv; ++i) {
- /* + 1 because we want to match the '=' character too. */
- if (STREQLEN(cmd->env[i], env, namelen + 1)) {
- VIR_FREE(cmd->env[i]);
- cmd->env[i] = env;
- return;
- }
- }
-
- /* Arg plus trailing NULL. */
- if (VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 1 + 1) < 0) {
- VIR_FREE(env);
- cmd->has_error = ENOMEM;
- return;
- }
-
- cmd->env[cmd->nenv++] = env;
-}
-
-/**
- * virCommandAddEnvFormat:
- * @cmd: the command to modify
- * @format: format of arguments, end result must be in name=value format
- * @...: arguments to be formatted
- *
- * Add an environment variable to the child created by a printf-style format.
- */
-void
-virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...)
-{
- char *env;
- va_list list;
-
- if (!cmd || cmd->has_error)
- return;
-
- va_start(list, format);
- if (virVasprintf(&env, format, list) < 0) {
- cmd->has_error = ENOMEM;
- va_end(list);
- return;
- }
- va_end(list);
-
- virCommandAddEnv(cmd, env);
-}
-
-/**
- * virCommandAddEnvPair:
- * @cmd: the command to modify
- * @name: variable name, must not contain =
- * @value: value to assign to name
- *
- * Add an environment variable to the child
- * using separate name & value strings
- */
-void
-virCommandAddEnvPair(virCommandPtr cmd, const char *name, const char *value)
-{
- virCommandAddEnvFormat(cmd, "%s=%s", name, value);
-}
-
-
-/**
- * virCommandAddEnvString:
- * @cmd: the command to modify
- * @str: name=value format
- *
- * Add an environment variable to the child
- * using a preformatted env string FOO=BAR
- */
-void
-virCommandAddEnvString(virCommandPtr cmd, const char *str)
-{
- char *env;
-
- if (!cmd || cmd->has_error)
- return;
-
- if (!(env = strdup(str))) {
- cmd->has_error = ENOMEM;
- return;
- }
-
- virCommandAddEnv(cmd, env);
-}
-
-
-/**
- * virCommandAddEnvBuffer:
- * @cmd: the command to modify
- * @buf: buffer that contains name=value string, which will be reset on return
- *
- * Convert a buffer containing preformatted name=value into an
- * environment variable of the child.
- * Correctly transfers memory errors or contents from buf to cmd.
- */
-void
-virCommandAddEnvBuffer(virCommandPtr cmd, virBufferPtr buf)
-{
- if (!cmd || cmd->has_error) {
- virBufferFreeAndReset(buf);
- return;
- }
-
- if (virBufferError(buf)) {
- cmd->has_error = ENOMEM;
- virBufferFreeAndReset(buf);
- return;
- }
- if (!virBufferUse(buf)) {
- cmd->has_error = EINVAL;
- return;
- }
-
- virCommandAddEnv(cmd, virBufferContentAndReset(buf));
-}
-
-
-/**
- * virCommandAddEnvPass:
- * @cmd: the command to modify
- * @name: the name to look up in current environment
- *
- * Pass an environment variable to the child
- * using current process' value
- */
-void
-virCommandAddEnvPass(virCommandPtr cmd, const char *name)
-{
- char *value;
- if (!cmd || cmd->has_error)
- return;
-
- value = getenv(name);
- if (value)
- virCommandAddEnvPair(cmd, name, value);
-}
-
-
-/**
- * virCommandAddEnvPassCommon:
- * @cmd: the command to modify
- *
- * Set LC_ALL to C, and propagate other essential environment
- * variables (such as PATH) from the parent process.
- */
-void
-virCommandAddEnvPassCommon(virCommandPtr cmd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- /* Attempt to Pre-allocate; allocation failure will be detected
- * later during virCommandAdd*. */
- ignore_value(VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 9));
-
- virCommandAddEnvPair(cmd, "LC_ALL", "C");
-
- virCommandAddEnvPass(cmd, "LD_PRELOAD");
- virCommandAddEnvPass(cmd, "LD_LIBRARY_PATH");
- virCommandAddEnvPass(cmd, "PATH");
- virCommandAddEnvPass(cmd, "HOME");
- virCommandAddEnvPass(cmd, "USER");
- virCommandAddEnvPass(cmd, "LOGNAME");
- virCommandAddEnvPass(cmd, "TMPDIR");
-}
-
-/**
- * virCommandAddArg:
- * @cmd: the command to modify
- * @val: the argument to add
- *
- * Add a command line argument to the child
- */
-void
-virCommandAddArg(virCommandPtr cmd, const char *val)
-{
- char *arg;
-
- if (!cmd || cmd->has_error)
- return;
-
- if (!(arg = strdup(val))) {
- cmd->has_error = ENOMEM;
- return;
- }
-
- /* Arg plus trailing NULL. */
- if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) {
- VIR_FREE(arg);
- cmd->has_error = ENOMEM;
- return;
- }
-
- cmd->args[cmd->nargs++] = arg;
-}
-
-
-/**
- * virCommandAddArgBuffer:
- * @cmd: the command to modify
- * @buf: buffer that contains argument string, which will be reset on return
- *
- * Convert a buffer into a command line argument to the child.
- * Correctly transfers memory errors or contents from buf to cmd.
- */
-void
-virCommandAddArgBuffer(virCommandPtr cmd, virBufferPtr buf)
-{
- if (!cmd || cmd->has_error) {
- virBufferFreeAndReset(buf);
- return;
- }
-
- /* Arg plus trailing NULL. */
- if (virBufferError(buf) ||
- VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) {
- cmd->has_error = ENOMEM;
- virBufferFreeAndReset(buf);
- return;
- }
-
- cmd->args[cmd->nargs] = virBufferContentAndReset(buf);
- if (!cmd->args[cmd->nargs])
- cmd->args[cmd->nargs] = strdup("");
- if (!cmd->args[cmd->nargs]) {
- cmd->has_error = ENOMEM;
- return;
- }
- cmd->nargs++;
-}
-
-
-/**
- * virCommandAddArgFormat:
- * @cmd: the command to modify
- * @format: format of arguments, end result must be in name=value format
- * @...: arguments to be formatted
- *
- * Add a command line argument created by a printf-style format.
- */
-void
-virCommandAddArgFormat(virCommandPtr cmd, const char *format, ...)
-{
- char *arg;
- va_list list;
-
- if (!cmd || cmd->has_error)
- return;
-
- 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) {
- VIR_FREE(arg);
- cmd->has_error = ENOMEM;
- return;
- }
-
- cmd->args[cmd->nargs++] = arg;
-}
-
-/**
- * virCommandAddArgPair:
- * @cmd: the command to modify
- * @name: left half of argument
- * @value: right half of argument
- *
- * 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);
-}
-
-/**
- * virCommandAddArgSet:
- * @cmd: the command to modify
- * @vals: array of arguments to add
- *
- * Add a NULL terminated list of args
- */
-void
-virCommandAddArgSet(virCommandPtr cmd, const char *const*vals)
-{
- int narg = 0;
-
- if (!cmd || cmd->has_error)
- return;
-
- if (vals[0] == NULL) {
- cmd->has_error = EINVAL;
- return;
- }
-
- while (vals[narg] != NULL)
- narg++;
-
- /* narg plus trailing NULL. */
- if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1) < 0) {
- cmd->has_error = ENOMEM;
- return;
- }
-
- narg = 0;
- while (vals[narg] != NULL) {
- char *arg = strdup(vals[narg++]);
- if (!arg) {
- cmd->has_error = ENOMEM;
- return;
- }
- cmd->args[cmd->nargs++] = arg;
- }
-}
-
-/**
- * virCommandAddArgList:
- * @cmd: the command to modify
- * @...: list of arguments to add
- *
- * Add a NULL terminated list of args.
- */
-void
-virCommandAddArgList(virCommandPtr cmd, ...)
-{
- va_list list;
- int narg = 0;
-
- if (!cmd || cmd->has_error)
- return;
-
- va_start(list, cmd);
- while (va_arg(list, const char *) != NULL)
- narg++;
- va_end(list);
-
- /* narg plus trailing NULL. */
- if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1) < 0) {
- cmd->has_error = ENOMEM;
- return;
- }
-
- va_start(list, cmd);
- while (1) {
- char *arg = va_arg(list, char *);
- if (!arg)
- break;
- arg = strdup(arg);
- if (!arg) {
- cmd->has_error = ENOMEM;
- va_end(list);
- return;
- }
- cmd->args[cmd->nargs++] = arg;
- }
- va_end(list);
-}
-
-/**
- * virCommandSetWorkingDirectory:
- * @cmd: the command to modify
- * @pwd: directory to use
- *
- * Set the working directory of a non-daemon child process, rather
- * than the parent's working directory. Daemons automatically get /
- * without using this call.
- */
-void
-virCommandSetWorkingDirectory(virCommandPtr cmd, const char *pwd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->pwd) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot set directory twice");
- } else {
- cmd->pwd = strdup(pwd);
- if (!cmd->pwd)
- cmd->has_error = ENOMEM;
- }
-}
-
-
-/**
- * virCommandSetInputBuffer:
- * @cmd: the command to modify
- * @inbuf: string to feed to stdin
- *
- * Feed the child's stdin from a string buffer. This requires the use
- * of virCommandRun().
- */
-void
-virCommandSetInputBuffer(virCommandPtr cmd, const char *inbuf)
-{
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->infd != -1 || cmd->inbuf) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify input twice");
- return;
- }
-
- cmd->inbuf = strdup(inbuf);
- if (!cmd->inbuf)
- cmd->has_error = ENOMEM;
-}
-
-
-/**
- * virCommandSetOutputBuffer:
- * @cmd: the command to modify
- * @outbuf: address of variable to store malloced result buffer
- *
- * Capture the child's stdout to a string buffer. *outbuf is
- * guaranteed to be allocated after successful virCommandRun or
- * virCommandWait, and is best-effort allocated after failed
- * virCommandRun; caller is responsible for freeing *outbuf.
- * This requires the use of virCommandRun.
- */
-void
-virCommandSetOutputBuffer(virCommandPtr cmd, char **outbuf)
-{
- *outbuf = NULL;
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->outfdptr) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify output twice");
- return;
- }
-
- cmd->outbuf = outbuf;
- cmd->outfdptr = &cmd->outfd;
-}
-
-
-/**
- * virCommandSetErrorBuffer:
- * @cmd: the command to modify
- * @errbuf: address of variable to store malloced result buffer
- *
- * Capture the child's stderr to a string buffer. *errbuf is
- * guaranteed to be allocated after successful virCommandRun or
- * virCommandWait, and is best-effort allocated after failed
- * virCommandRun; caller is responsible for freeing *errbuf.
- * This requires the use of virCommandRun. It is possible to
- * pass the same pointer as for virCommandSetOutputBuffer(), in
- * which case the child process will interleave all output into
- * a single string.
- */
-void
-virCommandSetErrorBuffer(virCommandPtr cmd, char **errbuf)
-{
- *errbuf = NULL;
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->errfdptr) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify stderr twice");
- return;
- }
-
- cmd->errbuf = errbuf;
- cmd->errfdptr = &cmd->errfd;
-}
-
-
-/**
- * virCommandSetInputFD:
- * @cmd: the command to modify
- * @infd: the descriptor to use
- *
- * Attach a file descriptor to the child's stdin
- */
-void
-virCommandSetInputFD(virCommandPtr cmd, int infd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->infd != -1 || cmd->inbuf) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify input twice");
- return;
- }
- if (infd < 0) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify invalid input fd");
- return;
- }
-
- cmd->infd = infd;
-}
-
-
-/**
- * virCommandSetOutputFD:
- * @cmd: the command to modify
- * @outfd: location of output fd
- *
- * Attach a file descriptor to the child's stdout. If *@outfd is -1 on
- * entry, then a pipe will be created and returned in this variable when
- * the child is run. Otherwise, *@outfd is used as the output.
- */
-void
-virCommandSetOutputFD(virCommandPtr cmd, int *outfd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->outfdptr) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify output twice");
- return;
- }
-
- cmd->outfdptr = outfd;
-}
-
-
-/**
- * virCommandSetErrorFD:
- * @cmd: the command to modify
- * @errfd: location of error fd
- *
- * Attach a file descriptor to the child's stderr. If *@errfd is -1 on
- * entry, then a pipe will be created and returned in this variable when
- * the child is run. Otherwise, *@errfd is used for error collection,
- * and may be the same as outfd given to virCommandSetOutputFD().
- */
-void
-virCommandSetErrorFD(virCommandPtr cmd, int *errfd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->errfdptr) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify stderr twice");
- return;
- }
-
- cmd->errfdptr = errfd;
-}
-
-
-/**
- * virCommandSetPreExecHook:
- * @cmd: the command to modify
- * @hook: the hook to run
- * @opaque: argument to pass to the hook
- *
- * Run HOOK(OPAQUE) in the child as the last thing before changing
- * directories, dropping capabilities, and executing the new process.
- * Force the child to fail if HOOK does not return zero.
- *
- * Since @hook runs in the child, it should be careful to avoid
- * any functions that are not async-signal-safe.
- */
-void
-virCommandSetPreExecHook(virCommandPtr cmd, virExecHook hook, void *opaque)
-{
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->hook) {
- cmd->has_error = -1;
- VIR_DEBUG("cannot specify hook twice");
- return;
- }
- cmd->hook = hook;
- cmd->opaque = opaque;
-}
-
-
-/**
- * virCommandWriteArgLog:
- * @cmd: the command to log
- * @logfd: where to log the results
- *
- * 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)));
- }
-}
-
-
-/**
- * virCommandToString:
- * @cmd: the command to convert
- *
- * Call after adding all arguments and environment settings, but
- * before Run/RunAsync, to return a string representation of the
- * environment and arguments of cmd, suitably quoted for pasting into
- * a shell. 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 == ENOMEM) {
- virReportOOMError();
- return NULL;
- }
- if (cmd->has_error) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return NULL;
- }
-
- for (i = 0; i < cmd->nenv; i++) {
- /* In shell, a='b c' has a different meaning than 'a=b c', so
- * we must determine where the '=' lives. */
- char *eq = strchr(cmd->env[i], '=');
-
- if (!eq) {
- virBufferFreeAndReset(&buf);
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return NULL;
- }
- eq++;
- virBufferAdd(&buf, cmd->env[i], eq - cmd->env[i]);
- virBufferEscapeShell(&buf, eq);
- virBufferAddChar(&buf, ' ');
- }
- virBufferEscapeShell(&buf, cmd->args[0]);
- for (i = 1; i < cmd->nargs; i++) {
- virBufferAddChar(&buf, ' ');
- virBufferEscapeShell(&buf, cmd->args[i]);
- }
-
- if (virBufferError(&buf)) {
- virBufferFreeAndReset(&buf);
- virReportOOMError();
- return NULL;
- }
-
- return virBufferContentAndReset(&buf);
-}
-
-
-/*
- * Manage input and output to the child process.
- */
-static int
-virCommandProcessIO(virCommandPtr cmd, int *inpipe)
-{
- int infd = -1, outfd = -1, errfd = -1;
- size_t inlen = 0, outlen = 0, errlen = 0;
- size_t inoff = 0;
- int ret = 0;
-
- /* With an input buffer, feed data to child
- * via pipe */
- if (cmd->inbuf) {
- inlen = strlen(cmd->inbuf);
- infd = *inpipe;
- }
-
- /* With out/err buffer, the outfd/errfd have been filled with an
- * FD for us. Guarantee an allocated string with partial results
- * even if we encounter a later failure, as well as freeing any
- * results accumulated over a prior run of the same command. */
- if (cmd->outbuf) {
- outfd = cmd->outfd;
- if (VIR_REALLOC_N(*cmd->outbuf, 1) < 0) {
- virReportOOMError();
- ret = -1;
- }
- }
- if (cmd->errbuf) {
- errfd = cmd->errfd;
- if (VIR_REALLOC_N(*cmd->errbuf, 1) < 0) {
- virReportOOMError();
- ret = -1;
- }
- }
- if (ret == -1)
- goto cleanup;
- ret = -1;
-
- for (;;) {
- int i;
- struct pollfd fds[3];
- int nfds = 0;
-
- if (infd != -1) {
- fds[nfds].fd = infd;
- fds[nfds].events = POLLOUT;
- fds[nfds].revents = 0;
- nfds++;
- }
- if (outfd != -1) {
- fds[nfds].fd = outfd;
- fds[nfds].events = POLLIN;
- fds[nfds].revents = 0;
- nfds++;
- }
- if (errfd != -1) {
- fds[nfds].fd = errfd;
- fds[nfds].events = POLLIN;
- fds[nfds].revents = 0;
- nfds++;
- }
-
- if (nfds == 0)
- break;
-
- if (poll(fds, nfds, -1) < 0) {
- if (errno == EAGAIN || errno == EINTR)
- continue;
- virReportSystemError(errno, "%s",
- _("unable to poll on child"));
- goto cleanup;
- }
-
- for (i = 0; i < nfds ; i++) {
- if (fds[i].revents & (POLLIN | POLLHUP | POLLERR) &&
- (fds[i].fd == errfd || fds[i].fd == outfd)) {
- char data[1024];
- char **buf;
- size_t *len;
- int done;
- if (fds[i].fd == outfd) {
- buf = cmd->outbuf;
- len = &outlen;
- } else {
- buf = cmd->errbuf;
- len = &errlen;
- }
- /* Silence a false positive from clang. */
- sa_assert(buf);
-
- done = read(fds[i].fd, data, sizeof(data));
- if (done < 0) {
- if (errno != EINTR &&
- errno != EAGAIN) {
- virReportSystemError(errno, "%s",
- (fds[i].fd == outfd) ?
- _("unable to read child stdout")
:
- _("unable to read child
stderr"));
- goto cleanup;
- }
- } else if (done == 0) {
- if (fds[i].fd == outfd)
- outfd = -1;
- else
- errfd = -1;
- } else {
- if (VIR_REALLOC_N(*buf, *len + done + 1) < 0) {
- virReportOOMError();
- goto cleanup;
- }
- memcpy(*buf + *len, data, done);
- *len += done;
- }
- }
-
- if (fds[i].revents & (POLLOUT | POLLERR) &&
- fds[i].fd == infd) {
- int done;
-
- /* Coverity 5.3.0 can't see that we only get here if
- * infd is in the set because it was non-negative. */
- sa_assert(infd != -1);
- done = write(infd, cmd->inbuf + inoff,
- inlen - inoff);
- if (done < 0) {
- if (errno == EPIPE) {
- VIR_DEBUG("child closed stdin early, ignoring EPIPE "
- "on fd %d", infd);
- if (VIR_CLOSE(*inpipe) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", infd);
- infd = -1;
- } else if (errno != EINTR && errno != EAGAIN) {
- virReportSystemError(errno, "%s",
- _("unable to write to child
input"));
- goto cleanup;
- }
- } else {
- inoff += done;
- if (inoff == inlen) {
- if (VIR_CLOSE(*inpipe) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", infd);
- infd = -1;
- }
- }
- }
- }
- }
-
- ret = 0;
-cleanup:
- if (cmd->outbuf && *cmd->outbuf)
- (*cmd->outbuf)[outlen] = '\0';
- if (cmd->errbuf && *cmd->errbuf)
- (*cmd->errbuf)[errlen] = '\0';
- return ret;
-}
-
-/**
- * virCommandExec:
- * @cmd: command to run
- *
- * Exec the command, replacing the current process. Meant to be called
- * in the hook after already forking / cloning, so does not attempt to
- * daemonize or preserve any FDs.
- *
- * Returns -1 on any error executing the command.
- * Will not return on success.
- */
-#ifndef WIN32
-int virCommandExec(virCommandPtr cmd)
-{
- if (!cmd ||cmd->has_error == ENOMEM) {
- virReportOOMError();
- return -1;
- }
- if (cmd->has_error) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return -1;
- }
-
- return execve(cmd->args[0], cmd->args, cmd->env);
-}
-#else
-int virCommandExec(virCommandPtr cmd ATTRIBUTE_UNUSED)
-{
- /* Mingw execve() has a broken signature. Disable this
- * function until gnulib fixes the signature, since we
- * don't really need this on Win32 anyway.
- */
- virReportSystemError(ENOSYS, "%s",
- _("Executing new processes is not supported on Win32
platform"));
- return -1;
-}
-#endif
-
-/**
- * virCommandRun:
- * @cmd: command to run
- * @exitstatus: optional status collection
- *
- * Run the command and wait for completion.
- * Returns -1 on any error executing the
- * command. Returns 0 if the command executed,
- * with the exit status set. If @exitstatus is NULL, then the
- * child must exit with status 0 for this to succeed.
- */
-int
-virCommandRun(virCommandPtr cmd, int *exitstatus)
-{
- int ret = 0;
- char *outbuf = NULL;
- char *errbuf = NULL;
- int infd[2] = { -1, -1 };
- struct stat st;
- bool string_io;
- bool async_io = false;
- char *str;
- int tmpfd;
-
- if (!cmd ||cmd->has_error == ENOMEM) {
- virReportOOMError();
- return -1;
- }
- if (cmd->has_error) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return -1;
- }
-
- /* Avoid deadlock, by requiring that any open fd not under our
- * control must be visiting a regular file, or that we are
- * daemonized and no string io is required. */
- string_io = cmd->inbuf || cmd->outbuf || cmd->errbuf;
- if (cmd->infd != -1 &&
- (fstat(cmd->infd, &st) < 0 || !S_ISREG(st.st_mode)))
- async_io = true;
- if (cmd->outfdptr && cmd->outfdptr != &cmd->outfd &&
- (*cmd->outfdptr == -1 ||
- fstat(*cmd->outfdptr, &st) < 0 || !S_ISREG(st.st_mode)))
- async_io = true;
- if (cmd->errfdptr && cmd->errfdptr != &cmd->errfd &&
- (*cmd->errfdptr == -1 ||
- fstat(*cmd->errfdptr, &st) < 0 || !S_ISREG(st.st_mode)))
- async_io = true;
- if (async_io) {
- if (!(cmd->flags & VIR_EXEC_DAEMON) || string_io) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("cannot mix caller fds with blocking
execution"));
- return -1;
- }
- } else {
- if ((cmd->flags & VIR_EXEC_DAEMON) && string_io) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("cannot mix string I/O with daemon"));
- return -1;
- }
- }
-
- /* If we have an input buffer, we need
- * a pipe to feed the data to the child */
- if (cmd->inbuf) {
- if (pipe2(infd, O_CLOEXEC) < 0) {
- virReportSystemError(errno, "%s",
- _("unable to open pipe"));
- cmd->has_error = -1;
- return -1;
- }
- cmd->infd = infd[0];
- }
-
- /* If caller requested the same string for stdout and stderr, then
- * merge those into one string. */
- if (cmd->outbuf && cmd->outbuf == cmd->errbuf) {
- cmd->errfdptr = &cmd->outfd;
- cmd->errbuf = NULL;
- }
-
- /* If caller hasn't requested capture of stdout/err, then capture
- * it ourselves so we can log it. But the intermediate child for
- * a daemon has no expected output, and we don't want our
- * capturing pipes passed on to the daemon grandchild.
- */
- if (!(cmd->flags & VIR_EXEC_DAEMON)) {
- if (!cmd->outfdptr) {
- cmd->outfdptr = &cmd->outfd;
- cmd->outbuf = &outbuf;
- string_io = true;
- }
- if (!cmd->errfdptr) {
- cmd->errfdptr = &cmd->errfd;
- cmd->errbuf = &errbuf;
- string_io = true;
- }
- }
-
- cmd->flags |= VIR_EXEC_RUN_SYNC;
- if (virCommandRunAsync(cmd, NULL) < 0) {
- if (cmd->inbuf) {
- tmpfd = infd[0];
- if (VIR_CLOSE(infd[0]) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
- tmpfd = infd[1];
- if (VIR_CLOSE(infd[1]) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
- }
- cmd->has_error = -1;
- return -1;
- }
-
- tmpfd = infd[0];
- if (VIR_CLOSE(infd[0]) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
- if (string_io)
- ret = virCommandProcessIO(cmd, &infd[1]);
-
- if (virCommandWait(cmd, exitstatus) < 0)
- ret = -1;
-
- str = (exitstatus ? virProcessTranslateStatus(*exitstatus)
- : (char *) "status 0");
- VIR_DEBUG("Result %s, stdout: '%s' stderr: '%s'",
- NULLSTR(str),
- cmd->outbuf ? NULLSTR(*cmd->outbuf) : "(null)",
- cmd->errbuf ? NULLSTR(*cmd->errbuf) : "(null)");
- if (exitstatus)
- VIR_FREE(str);
-
- /* Reset any capturing, in case caller runs
- * this identical command again */
- if (cmd->inbuf) {
- tmpfd = infd[1];
- if (VIR_CLOSE(infd[1]) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
- }
- if (cmd->outbuf == &outbuf) {
- tmpfd = cmd->outfd;
- if (VIR_CLOSE(cmd->outfd) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
- cmd->outfdptr = NULL;
- cmd->outbuf = NULL;
- VIR_FREE(outbuf);
- }
- if (cmd->errbuf == &errbuf) {
- tmpfd = cmd->errfd;
- if (VIR_CLOSE(cmd->errfd) < 0)
- VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
- cmd->errfdptr = NULL;
- cmd->errbuf = NULL;
- VIR_FREE(errbuf);
- }
-
- return ret;
-}
-
-
-/*
- * Perform all virCommand-specific actions, along with the user hook.
- */
-static int
-virCommandHook(void *data)
-{
- virCommandPtr cmd = data;
- int res = 0;
-
- if (cmd->hook) {
- VIR_DEBUG("Run hook %p %p", cmd->hook, cmd->opaque);
- res = cmd->hook(cmd->opaque);
- VIR_DEBUG("Done hook %d", res);
- }
- if (res == 0 && cmd->pwd) {
- VIR_DEBUG("Running child in %s", cmd->pwd);
- res = chdir(cmd->pwd);
- if (res < 0) {
- virReportSystemError(errno,
- _("Unable to change to %s"), cmd->pwd);
- }
- }
- if (cmd->handshake) {
- char c = res < 0 ? '0' : '1';
- int rv;
- VIR_DEBUG("Notifying parent for handshake start on %d",
- cmd->handshakeWait[1]);
- if (safewrite(cmd->handshakeWait[1], &c, sizeof(c)) != sizeof(c)) {
- virReportSystemError(errno, "%s",
- _("Unable to notify parent process"));
- return -1;
- }
-
- /* On failure we pass the error message back to parent,
- * so they don't have to dig through stderr logs
- */
- if (res < 0) {
- virErrorPtr err = virGetLastError();
- const char *msg = err ? err->message :
- _("Unknown failure during hook execution");
- size_t len = strlen(msg) + 1;
- if (safewrite(cmd->handshakeWait[1], msg, len) != len) {
- virReportSystemError(errno, "%s",
- _("Unable to send error to parent"));
- return -1;
- }
- return -1;
- }
-
- VIR_DEBUG("Waiting on parent for handshake complete on %d",
- cmd->handshakeNotify[0]);
- if ((rv = saferead(cmd->handshakeNotify[0], &c,
- sizeof(c))) != sizeof(c)) {
- if (rv < 0)
- virReportSystemError(errno, "%s",
- _("Unable to wait on parent process"));
- else
- virReportSystemError(EIO, "%s",
- _("libvirtd quit during handshake"));
- return -1;
- }
- if (c != '1') {
- virReportSystemError(EINVAL,
- _("Unexpected confirm code '%c' from
parent"),
- c);
- return -1;
- }
- VIR_FORCE_CLOSE(cmd->handshakeWait[1]);
- VIR_FORCE_CLOSE(cmd->handshakeNotify[0]);
- }
-
- VIR_DEBUG("Hook is done %d", res);
-
- return res;
-}
-
-
-/**
- * virCommandRunAsync:
- * @cmd: command to start
- * @pid: optional variable to track child pid
- *
- * Run the command asynchronously
- * Returns -1 on any error executing the
- * command. Returns 0 if the command executed.
- *
- * There are two approaches to child process cleanup.
- * 1. Use auto-cleanup, by passing NULL for pid. The child will be
- * auto-reaped by virCommandFree, unless you reap it earlier via
- * virCommandWait or virCommandAbort. Good for where cmd is in
- * scope for the duration of the child process.
- * 2. Use manual cleanup, by passing the address of a pid_t variable
- * for pid. While cmd is still in scope, you may reap the child via
- * virCommandWait or virCommandAbort. But after virCommandFree, if
- * you have not yet reaped the child, then it continues to run until
- * you call virProcessWait or virProcessAbort.
- */
-int
-virCommandRunAsync(virCommandPtr cmd, pid_t *pid)
-{
- int ret;
- char *str;
- int i;
- bool synchronous = false;
-
- if (!cmd || cmd->has_error == ENOMEM) {
- virReportOOMError();
- return -1;
- }
- if (cmd->has_error) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return -1;
- }
-
- synchronous = cmd->flags & VIR_EXEC_RUN_SYNC;
- cmd->flags &= ~VIR_EXEC_RUN_SYNC;
-
- /* Buffer management can only be requested via virCommandRun. */
- if ((cmd->inbuf && cmd->infd == -1) ||
- (cmd->outbuf && cmd->outfdptr != &cmd->outfd) ||
- (cmd->errbuf && cmd->errfdptr != &cmd->errfd)) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("cannot mix string I/O with asynchronous command"));
- return -1;
- }
-
- if (cmd->pid != -1) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("command is already running as pid %lld"),
- (long long) cmd->pid);
- return -1;
- }
-
- if (!synchronous && (cmd->flags & VIR_EXEC_DAEMON)) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("daemonized command cannot use virCommandRunAsync"));
- return -1;
- }
- if (cmd->pwd && (cmd->flags & VIR_EXEC_DAEMON)) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("daemonized command cannot set working directory
%s"),
- cmd->pwd);
- return -1;
- }
- if (cmd->pidfile && !(cmd->flags & VIR_EXEC_DAEMON)) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("creation of pid file requires daemonized command"));
- return -1;
- }
-
- str = virCommandToString(cmd);
- VIR_DEBUG("About to run %s", str ? str : cmd->args[0]);
- VIR_FREE(str);
-
- ret = virExecWithHook((const char *const *)cmd->args,
- (const char *const *)cmd->env,
- cmd->preserve,
- cmd->preserve_size,
- &cmd->pid,
- cmd->infd,
- cmd->outfdptr,
- cmd->errfdptr,
- cmd->flags,
- virCommandHook,
- cmd,
- cmd->pidfile,
- cmd->capabilities);
-
- VIR_DEBUG("Command result %d, with PID %d",
- ret, (int)cmd->pid);
-
- for (i = 0; i < cmd->transfer_size; i++) {
- VIR_FORCE_CLOSE(cmd->transfer[i]);
- }
- cmd->transfer_size = 0;
- VIR_FREE(cmd->transfer);
-
- if (ret == 0 && pid)
- *pid = cmd->pid;
- else
- cmd->reap = true;
-
- return ret;
-}
-
-
-/**
- * virCommandWait:
- * @cmd: command to wait on
- * @exitstatus: optional status collection
- *
- * Wait for the command previously started with virCommandRunAsync()
- * to complete. Return -1 on any error waiting for
- * completion. Returns 0 if the command
- * finished with the exit status set. If @exitstatus is NULL, then the
- * child must exit with status 0 for this to succeed.
- */
-int
-virCommandWait(virCommandPtr cmd, int *exitstatus)
-{
- int ret;
- int status = 0;
-
- if (!cmd ||cmd->has_error == ENOMEM) {
- virReportOOMError();
- return -1;
- }
- if (cmd->has_error) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return -1;
- }
-
- if (cmd->pid == -1) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("command is not yet running"));
- return -1;
- }
-
- /* If virProcessWait reaps pid but then returns failure because
- * exitstatus was NULL, then a second virCommandWait would risk
- * calling waitpid on an unrelated process. Besides, that error
- * message is not as detailed as what we can provide. So, we
- * guarantee that virProcessWait only fails due to failure to wait,
- * and repeat the exitstatus check code ourselves. */
- ret = virProcessWait(cmd->pid, exitstatus ? exitstatus : &status);
- if (ret == 0) {
- cmd->pid = -1;
- cmd->reap = false;
- if (status) {
- char *str = virCommandToString(cmd);
- char *st = virProcessTranslateStatus(status);
- bool haveErrMsg = cmd->errbuf && *cmd->errbuf &&
(*cmd->errbuf)[0];
-
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("Child process (%s) unexpected %s%s%s"),
- str ? str : cmd->args[0], NULLSTR(st),
- haveErrMsg ? ": " : "",
- haveErrMsg ? *cmd->errbuf : "");
- VIR_FREE(str);
- VIR_FREE(st);
- return -1;
- }
- }
-
- return ret;
-}
-
-
-#ifndef WIN32
-/**
- * virCommandAbort:
- * @cmd: command to abort
- *
- * Abort an async command if it is running, without issuing
- * any errors or affecting errno. Designed for error paths
- * where some but not all paths to the cleanup code might
- * have started the child process.
- */
-void
-virCommandAbort(virCommandPtr cmd)
-{
- if (!cmd || cmd->pid == -1)
- return;
- virProcessAbort(cmd->pid);
- cmd->pid = -1;
- cmd->reap = false;
-}
-#else /* WIN32 */
-void
-virCommandAbort(virCommandPtr cmd ATTRIBUTE_UNUSED)
-{
- /* Mingw lacks WNOHANG and kill(). But since we haven't ported
- * virExecWithHook to mingw yet, there's no process to be killed,
- * making this implementation trivially correct for now :) */
-}
-#endif
-
-
-/**
- * virCommandRequireHandshake:
- * @cmd: command to modify
- *
- * Request that the child perform a handshake with
- * the parent when the hook function has completed
- * execution. The child will not exec() until the
- * parent has notified
- */
-void virCommandRequireHandshake(virCommandPtr cmd)
-{
- if (!cmd || cmd->has_error)
- return;
-
- if (cmd->handshake) {
- cmd->has_error = -1;
- VIR_DEBUG("Cannot require handshake twice");
- return;
- }
-
- if (pipe2(cmd->handshakeWait, O_CLOEXEC) < 0) {
- cmd->has_error = errno;
- return;
- }
- if (pipe2(cmd->handshakeNotify, O_CLOEXEC) < 0) {
- VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
- VIR_FORCE_CLOSE(cmd->handshakeWait[1]);
- cmd->has_error = errno;
- return;
- }
-
- VIR_DEBUG("Transfer handshake wait=%d notify=%d, "
- "keep handshake wait=%d notify=%d",
- cmd->handshakeWait[1], cmd->handshakeNotify[0],
- cmd->handshakeWait[0], cmd->handshakeNotify[1]);
- virCommandTransferFD(cmd, cmd->handshakeWait[1]);
- virCommandTransferFD(cmd, cmd->handshakeNotify[0]);
- cmd->handshake = true;
-}
-
-/**
- * virCommandHandshakeWait:
- * @cmd: command to wait on
- *
- * Wait for the child to complete execution of its
- * hook function. To be called in the parent.
- */
-int virCommandHandshakeWait(virCommandPtr cmd)
-{
- char c;
- int rv;
- if (!cmd ||cmd->has_error == ENOMEM) {
- virReportOOMError();
- return -1;
- }
- if (cmd->has_error || !cmd->handshake) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return -1;
- }
-
- if (cmd->handshakeWait[0] == -1) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Handshake is already complete"));
- return -1;
- }
-
- VIR_DEBUG("Wait for handshake on %d", cmd->handshakeWait[0]);
- if ((rv = saferead(cmd->handshakeWait[0], &c, sizeof(c))) != sizeof(c)) {
- if (rv < 0)
- virReportSystemError(errno, "%s",
- _("Unable to wait for child process"));
- else
- virReportSystemError(EIO, "%s",
- _("Child quit during startup handshake"));
- VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
- return -1;
- }
- if (c != '1') {
- char *msg;
- ssize_t len;
- if (VIR_ALLOC_N(msg, 1024) < 0) {
- virReportOOMError();
- VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
- return -1;
- }
- /* Close the handshakeNotify fd before trying to read anything
- * further on the handshakeWait pipe; so that a child waiting
- * on our acknowledgment will die rather than deadlock. */
- VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
-
- if ((len = saferead(cmd->handshakeWait[0], msg, 1024)) < 0) {
- VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
- VIR_FREE(msg);
- virReportSystemError(errno, "%s",
- _("No error message from child failure"));
- return -1;
- }
- VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
- msg[len-1] = '\0';
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
- VIR_FREE(msg);
- return -1;
- }
- VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
- return 0;
-}
-
-/**
- * virCommandHandshakeNotify:
- * @cmd: command to resume
- *
- * Notify the child that it is OK to exec() the
- * real binary now. To be called in the parent.
- */
-int virCommandHandshakeNotify(virCommandPtr cmd)
-{
- char c = '1';
- if (!cmd ||cmd->has_error == ENOMEM) {
- virReportOOMError();
- return -1;
- }
- if (cmd->has_error || !cmd->handshake) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("invalid use of command API"));
- return -1;
- }
-
- if (cmd->handshakeNotify[1] == -1) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Handshake is already complete"));
- return -1;
- }
-
- VIR_DEBUG("Notify handshake on %d", cmd->handshakeNotify[1]);
- if (safewrite(cmd->handshakeNotify[1], &c, sizeof(c)) != sizeof(c)) {
- virReportSystemError(errno, "%s", _("Unable to notify child
process"));
- VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
- return -1;
- }
- VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
- return 0;
-}
-
-
-/**
- * virCommandFree:
- * @cmd: optional command to free
- *
- * Release all resources. The only exception is that if you called
- * virCommandRunAsync with a non-null pid, then the asynchronous child
- * is not reaped, and you must call virProcessWait() or virProcessAbort() yourself.
- */
-void
-virCommandFree(virCommandPtr cmd)
-{
- int i;
- if (!cmd)
- return;
-
- for (i = 0; i < cmd->transfer_size; i++) {
- VIR_FORCE_CLOSE(cmd->transfer[i]);
- }
-
- VIR_FREE(cmd->inbuf);
- VIR_FORCE_CLOSE(cmd->outfd);
- VIR_FORCE_CLOSE(cmd->errfd);
-
- for (i = 0 ; i < cmd->nargs ; i++)
- VIR_FREE(cmd->args[i]);
- VIR_FREE(cmd->args);
-
- for (i = 0 ; i < cmd->nenv ; i++)
- VIR_FREE(cmd->env[i]);
- VIR_FREE(cmd->env);
-
- VIR_FREE(cmd->pwd);
-
- if (cmd->handshake) {
- /* The other 2 fds in these arrays are closed
- * due to use with virCommandTransferFD
- */
- VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
- VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
- }
-
- VIR_FREE(cmd->pidfile);
-
- if (cmd->reap)
- virCommandAbort(cmd);
-
- VIR_FREE(cmd->transfer);
- VIR_FREE(cmd->preserve);
-
- VIR_FREE(cmd);
-}
diff --git a/src/util/command.h b/src/util/command.h
deleted file mode 100644
index 6c8ab49..0000000
--- a/src/util/command.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * command.h: Child command execution
- *
- * Copyright (C) 2010-2011 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see
- * <
http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef __VIR_COMMAND_H__
-# define __VIR_COMMAND_H__
-
-# include "internal.h"
-# include "util.h"
-# include "virbuffer.h"
-
-typedef struct _virCommand virCommand;
-typedef virCommand *virCommandPtr;
-
-/* This will execute in the context of the first child
- * after fork() but before execve(). As such, it is unsafe to
- * call any function that is not async-signal-safe. */
-typedef int (*virExecHook)(void *data);
-
-int virFork(pid_t *pid) ATTRIBUTE_RETURN_CHECK;
-
-int virRun(const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK;
-
-virCommandPtr virCommandNew(const char *binary) ATTRIBUTE_NONNULL(1);
-
-virCommandPtr virCommandNewArgs(const char *const*args) ATTRIBUTE_NONNULL(1);
-
-virCommandPtr virCommandNewArgList(const char *binary, ...)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL;
-
-virCommandPtr virCommandNewVAList(const char *binary, va_list list)
- ATTRIBUTE_NONNULL(1);
-
-/* All error report from these setup APIs is
- * delayed until the Run/RunAsync methods
- */
-
-void virCommandPreserveFD(virCommandPtr cmd,
- int fd);
-
-void virCommandTransferFD(virCommandPtr cmd,
- int fd);
-
-void virCommandSetPidFile(virCommandPtr cmd,
- const char *pidfile) ATTRIBUTE_NONNULL(2);
-
-void virCommandClearCaps(virCommandPtr cmd);
-
-void virCommandAllowCap(virCommandPtr cmd,
- int capability);
-
-void virCommandDaemonize(virCommandPtr cmd);
-
-void virCommandNonblockingFDs(virCommandPtr cmd);
-
-void virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...)
- ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
-
-void virCommandAddEnvPair(virCommandPtr cmd,
- const char *name,
- const char *value) ATTRIBUTE_NONNULL(2);
-
-void virCommandAddEnvString(virCommandPtr cmd,
- const char *str) ATTRIBUTE_NONNULL(2);
-
-void virCommandAddEnvBuffer(virCommandPtr cmd,
- virBufferPtr buf);
-
-void virCommandAddEnvPass(virCommandPtr cmd,
- const char *name) ATTRIBUTE_NONNULL(2);
-
-void virCommandAddEnvPassCommon(virCommandPtr cmd);
-
-void virCommandAddArg(virCommandPtr cmd,
- const char *val) ATTRIBUTE_NONNULL(2);
-
-void virCommandAddArgBuffer(virCommandPtr cmd,
- virBufferPtr buf);
-
-void virCommandAddArgFormat(virCommandPtr cmd,
- const char *format, ...)
- ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
-
-void virCommandAddArgPair(virCommandPtr cmd,
- const char *name,
- const char *val)
- ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
-
-void virCommandAddArgSet(virCommandPtr cmd,
- const char *const*vals) ATTRIBUTE_NONNULL(2);
-
-void virCommandAddArgList(virCommandPtr cmd,
- ... /* const char *arg, ..., NULL */)
- ATTRIBUTE_SENTINEL;
-
-void virCommandSetWorkingDirectory(virCommandPtr cmd,
- const char *pwd) ATTRIBUTE_NONNULL(2);
-
-void virCommandSetInputBuffer(virCommandPtr cmd,
- const char *inbuf) ATTRIBUTE_NONNULL(2);
-
-void virCommandSetOutputBuffer(virCommandPtr cmd,
- char **outbuf) ATTRIBUTE_NONNULL(2);
-
-void virCommandSetErrorBuffer(virCommandPtr cmd,
- char **errbuf) ATTRIBUTE_NONNULL(2);
-
-void virCommandSetInputFD(virCommandPtr cmd,
- int infd);
-
-void virCommandSetOutputFD(virCommandPtr cmd,
- int *outfd) ATTRIBUTE_NONNULL(2);
-
-void virCommandSetErrorFD(virCommandPtr cmd,
- int *errfd) ATTRIBUTE_NONNULL(2);
-
-void virCommandSetPreExecHook(virCommandPtr cmd,
- virExecHook hook,
- void *opaque) ATTRIBUTE_NONNULL(2);
-
-void virCommandWriteArgLog(virCommandPtr cmd,
- int logfd);
-
-char *virCommandToString(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK;
-
-int virCommandExec(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK;
-
-int virCommandRun(virCommandPtr cmd,
- int *exitstatus) ATTRIBUTE_RETURN_CHECK;
-
-int virCommandRunAsync(virCommandPtr cmd,
- pid_t *pid) ATTRIBUTE_RETURN_CHECK;
-
-int virCommandWait(virCommandPtr cmd,
- int *exitstatus) ATTRIBUTE_RETURN_CHECK;
-
-void virCommandRequireHandshake(virCommandPtr cmd);
-
-int virCommandHandshakeWait(virCommandPtr cmd)
- ATTRIBUTE_RETURN_CHECK;
-
-int virCommandHandshakeNotify(virCommandPtr cmd)
- ATTRIBUTE_RETURN_CHECK;
-
-void virCommandAbort(virCommandPtr cmd);
-
-void virCommandFree(virCommandPtr cmd);
-
-#endif /* __VIR_COMMAND_H__ */
diff --git a/src/util/dnsmasq.c b/src/util/dnsmasq.c
index 74593c8..6e9c9dd 100644
--- a/src/util/dnsmasq.c
+++ b/src/util/dnsmasq.c
@@ -42,7 +42,7 @@
#include "virbitmap.h"
#include "dnsmasq.h"
#include "util.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "virterror_internal.h"
#include "logging.h"
diff --git a/src/util/ebtables.c b/src/util/ebtables.c
index f1b2986..4b427ee 100644
--- a/src/util/ebtables.c
+++ b/src/util/ebtables.c
@@ -41,7 +41,7 @@
#include "internal.h"
#include "ebtables.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "virterror_internal.h"
#include "logging.h"
diff --git a/src/util/hooks.c b/src/util/hooks.c
index 8817a4e..a6c056d 100644
--- a/src/util/hooks.c
+++ b/src/util/hooks.c
@@ -37,7 +37,7 @@
#include "memory.h"
#include "virfile.h"
#include "configmake.h"
-#include "command.h"
+#include "vircommand.h"
#define VIR_FROM_THIS VIR_FROM_HOOK
diff --git a/src/util/iptables.c b/src/util/iptables.c
index 00a1c29..25253ff 100644
--- a/src/util/iptables.c
+++ b/src/util/iptables.c
@@ -39,7 +39,7 @@
#include "internal.h"
#include "iptables.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "virterror_internal.h"
#include "logging.h"
diff --git a/src/util/pci.c b/src/util/pci.c
index 5971764..bf46fca 100644
--- a/src/util/pci.c
+++ b/src/util/pci.c
@@ -36,7 +36,7 @@
#include "logging.h"
#include "memory.h"
-#include "command.h"
+#include "vircommand.h"
#include "virterror_internal.h"
#include "virfile.h"
diff --git a/src/util/storage_file.c b/src/util/storage_file.c
index 3f85e0e..eebf59a 100644
--- a/src/util/storage_file.c
+++ b/src/util/storage_file.c
@@ -24,7 +24,6 @@
#include <config.h>
#include "storage_file.h"
-#include <command.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
@@ -41,6 +40,7 @@
#include "logging.h"
#include "virfile.h"
#include "c-ctype.h"
+#include "vircommand.h"
#include "virhash.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
diff --git a/src/util/sysinfo.c b/src/util/sysinfo.c
index bac4b23..e21cbfd 100644
--- a/src/util/sysinfo.c
+++ b/src/util/sysinfo.c
@@ -35,7 +35,7 @@
#include "util.h"
#include "logging.h"
#include "memory.h"
-#include "command.h"
+#include "vircommand.h"
#define VIR_FROM_THIS VIR_FROM_SYSINFO
diff --git a/src/util/util.c b/src/util/util.c
index 422ee75..f8ba7b4 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -84,7 +84,7 @@
#include "threads.h"
#include "verify.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "nonblocking.h"
#include "passfd.h"
#include "virprocess.h"
diff --git a/src/util/vircommand.c b/src/util/vircommand.c
new file mode 100644
index 0000000..3046658
--- /dev/null
+++ b/src/util/vircommand.c
@@ -0,0 +1,2523 @@
+/*
+ * vircommand.c: Child command execution
+ *
+ * Copyright (C) 2010-2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#if HAVE_CAPNG
+# include <cap-ng.h>
+#endif
+
+#include "vircommand.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "util.h"
+#include "logging.h"
+#include "virfile.h"
+#include "virpidfile.h"
+#include "virprocess.h"
+#include "virbuffer.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+/* Flags for virExecWithHook */
+enum {
+ VIR_EXEC_NONE = 0,
+ VIR_EXEC_NONBLOCK = (1 << 0),
+ VIR_EXEC_DAEMON = (1 << 1),
+ VIR_EXEC_CLEAR_CAPS = (1 << 2),
+ VIR_EXEC_RUN_SYNC = (1 << 3),
+};
+
+struct _virCommand {
+ int has_error; /* ENOMEM on allocation failure, -1 for anything else. */
+
+ char **args;
+ size_t nargs;
+ size_t maxargs;
+
+ char **env;
+ size_t nenv;
+ size_t maxenv;
+
+ char *pwd;
+
+ int *preserve; /* FDs to pass to child. */
+ int preserve_size;
+ int *transfer; /* FDs to close in parent. */
+ int transfer_size;
+
+ unsigned int flags;
+
+ char *inbuf;
+ char **outbuf;
+ char **errbuf;
+
+ int infd;
+ int outfd;
+ int errfd;
+ int *outfdptr;
+ int *errfdptr;
+
+ bool handshake;
+ int handshakeWait[2];
+ int handshakeNotify[2];
+
+ virExecHook hook;
+ void *opaque;
+
+ pid_t pid;
+ char *pidfile;
+ bool reap;
+
+ unsigned long long capabilities;
+};
+
+/*
+ * virCommandFDIsSet:
+ * @fd: FD to test
+ * @set: the set
+ * @set_size: actual size of @set
+ *
+ * Check if FD is already in @set or not.
+ *
+ * Returns true if @set contains @fd,
+ * false otherwise.
+ */
+static bool
+virCommandFDIsSet(int fd,
+ const int *set,
+ int set_size)
+{
+ int i = 0;
+
+ while (i < set_size)
+ if (set[i++] == fd)
+ return true;
+
+ return false;
+}
+
+/*
+ * virCommandFDSet:
+ * @fd: FD to be put into @set
+ * @set: the set
+ * @set_size: actual size of @set
+ *
+ * This is practically generalized implementation
+ * of FD_SET() as we do not want to be limited
+ * by FD_SETSIZE.
+ *
+ * Returns: 0 on success,
+ * -1 on usage error,
+ * ENOMEM on OOM
+ */
+static int
+virCommandFDSet(int fd,
+ int **set,
+ int *set_size)
+{
+ if (fd < 0 || !set || !set_size)
+ return -1;
+
+ if (virCommandFDIsSet(fd, *set, *set_size))
+ return 0;
+
+ if (VIR_REALLOC_N(*set, *set_size + 1) < 0) {
+ return ENOMEM;
+ }
+
+ (*set)[*set_size] = fd;
+ (*set_size)++;
+
+ return 0;
+}
+
+#ifndef WIN32
+
+static int virClearCapabilities(void) ATTRIBUTE_UNUSED;
+
+# if HAVE_CAPNG
+static int virClearCapabilities(void)
+{
+ int ret;
+
+ capng_clear(CAPNG_SELECT_BOTH);
+
+ if ((ret = capng_apply(CAPNG_SELECT_BOTH)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot clear process capabilities %d"), ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * virSetCapabilities:
+ * @capabilities - capability flag to set.
+ * In case of 0, this function is identical to
+ * virClearCapabilities()
+ *
+ */
+static int virSetCapabilities(unsigned long long capabilities)
+{
+ int ret, i;
+
+ capng_clear(CAPNG_SELECT_BOTH);
+
+ for (i = 0; i <= CAP_LAST_CAP; i++) {
+ if (capabilities & (1ULL << i))
+ capng_update(CAPNG_ADD, CAPNG_BOUNDING_SET, i);
+ }
+
+ if ((ret = capng_apply(CAPNG_SELECT_BOTH)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot apply process capabilities %d"), ret);
+ return -1;
+ }
+
+ return 0;
+}
+# else
+static int virClearCapabilities(void)
+{
+// VIR_WARN("libcap-ng support not compiled in, unable to clear "
+// "capabilities");
+ return 0;
+}
+
+static int
+virSetCapabilities(unsigned long long capabilities ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+# endif
+
+/**
+ * virFork:
+ * @pid - a pointer to a pid_t that will receive the return value from
+ * fork()
+ *
+ * fork a new process while avoiding various race/deadlock conditions
+ *
+ * on return from virFork(), if *pid < 0, the fork failed and there is
+ * no new process. Otherwise, just like fork(), if *pid == 0, it is the
+ * child process returning, and if *pid > 0, it is the parent.
+ *
+ * Even if *pid >= 0, if the return value from virFork() is < 0, it
+ * indicates a failure that occurred in the parent or child process
+ * after the fork. In this case, the child process should call
+ * _exit(EXIT_FAILURE) after doing any additional error reporting.
+ */
+int
+virFork(pid_t *pid)
+{
+ sigset_t oldmask, newmask;
+ struct sigaction sig_action;
+ int saved_errno, ret = -1;
+
+ *pid = -1;
+
+ /*
+ * Need to block signals now, so that child process can safely
+ * kill off caller's signal handlers without a race.
+ */
+ sigfillset(&newmask);
+ if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
+ saved_errno = errno;
+ virReportSystemError(errno,
+ "%s", _("cannot block signals"));
+ goto cleanup;
+ }
+
+ /* Ensure we hold the logging lock, to protect child processes
+ * from deadlocking on another thread's inherited mutex state */
+ virLogLock();
+
+ *pid = fork();
+ saved_errno = errno; /* save for caller */
+
+ /* Unlock for both parent and child process */
+ virLogUnlock();
+
+ if (*pid < 0) {
+ /* attempt to restore signal mask, but ignore failure, to
+ avoid obscuring the fork failure */
+ ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL));
+ virReportSystemError(saved_errno,
+ "%s", _("cannot fork child process"));
+ goto cleanup;
+ }
+
+ if (*pid) {
+
+ /* parent process */
+
+ /* Restore our original signal mask now that the child is
+ safely running */
+ if (pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0) {
+ saved_errno = errno; /* save for caller */
+ virReportSystemError(errno, "%s", _("cannot unblock
signals"));
+ goto cleanup;
+ }
+ ret = 0;
+
+ } else {
+
+ /* child process */
+
+ int logprio;
+ int i;
+
+ /* Remove any error callback so errors in child now
+ get sent to stderr where they stand a fighting chance
+ of being seen / logged */
+ virSetErrorFunc(NULL, NULL);
+ virSetErrorLogPriorityFunc(NULL);
+
+ /* Make sure any hook logging is sent to stderr, since child
+ * process may close the logfile FDs */
+ logprio = virLogGetDefaultPriority();
+ virLogReset();
+ virLogSetDefaultPriority(logprio);
+
+ /* Clear out all signal handlers from parent so nothing
+ unexpected can happen in our child once we unblock
+ signals */
+ sig_action.sa_handler = SIG_DFL;
+ sig_action.sa_flags = 0;
+ sigemptyset(&sig_action.sa_mask);
+
+ for (i = 1; i < NSIG; i++) {
+ /* Only possible errors are EFAULT or EINVAL
+ The former wont happen, the latter we
+ expect, so no need to check return value */
+
+ sigaction(i, &sig_action, NULL);
+ }
+
+ /* Unmask all signals in child, since we've no idea
+ what the caller's done with their signal mask
+ and don't want to propagate that to children */
+ sigemptyset(&newmask);
+ if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) {
+ saved_errno = errno; /* save for caller */
+ virReportSystemError(errno, "%s", _("cannot unblock
signals"));
+ goto cleanup;
+ }
+ ret = 0;
+ }
+
+cleanup:
+ if (ret < 0)
+ errno = saved_errno;
+ return ret;
+}
+
+/*
+ * Ensure that *null is an fd visiting /dev/null. Return 0 on
+ * success, -1 on failure. Allows for lazy opening of shared
+ * /dev/null fd only as required.
+ */
+static int
+getDevNull(int *null)
+{
+ if (*null == -1 && (*null = open("/dev/null", O_RDWR|O_CLOEXEC))
< 0) {
+ virReportSystemError(errno,
+ _("cannot open %s"),
+ "/dev/null");
+ return -1;
+ }
+ return 0;
+}
+
+/* Ensure that STD is an inheritable copy of FD. Return 0 on success,
+ * -1 on failure. */
+static int
+prepareStdFd(int fd, int std)
+{
+ if (fd == std)
+ return virSetInherit(fd, true);
+ if (dup2(fd, std) != std)
+ return -1;
+ return 0;
+}
+
+/*
+ * @argv argv to exec
+ * @envp optional environment to use for exec
+ * @keepfd options fd_ret to keep open for child process
+ * @retpid optional pointer to store child process pid
+ * @infd optional file descriptor to use as child input, otherwise /dev/null
+ * @outfd optional pointer to communicate output fd behavior
+ * outfd == NULL : Use /dev/null
+ * *outfd == -1 : Use a new fd
+ * *outfd != -1 : Use *outfd
+ * @errfd optional pointer to communcate error fd behavior. See outfd
+ * @flags possible combination of the following:
+ * VIR_EXEC_NONE : Default function behavior
+ * VIR_EXEC_NONBLOCK : Set child process output fd's as non-blocking
+ * VIR_EXEC_DAEMON : Daemonize the child process
+ * @hook optional virExecHook function to call prior to exec
+ * @data data to pass to the hook function
+ * @pidfile path to use as pidfile for daemonized process (needs DAEMON flag)
+ * @capabilities capabilities to keep
+ */
+static int
+virExecWithHook(const char *const*argv,
+ const char *const*envp,
+ const int *keepfd,
+ int keepfd_size,
+ pid_t *retpid,
+ int infd, int *outfd, int *errfd,
+ unsigned int flags,
+ virExecHook hook,
+ void *data,
+ char *pidfile,
+ unsigned long long capabilities)
+{
+ pid_t pid;
+ int null = -1, i, openmax;
+ int pipeout[2] = {-1,-1};
+ int pipeerr[2] = {-1,-1};
+ int childout = -1;
+ int childerr = -1;
+ int tmpfd;
+ const char *binary = NULL;
+ int forkRet;
+
+ if (argv[0][0] != '/') {
+ if (!(binary = virFindFileInPath(argv[0]))) {
+ virReportSystemError(ENOENT,
+ _("Cannot find '%s' in path"),
+ argv[0]);
+ return -1;
+ }
+ } else {
+ binary = argv[0];
+ }
+
+ if (infd < 0) {
+ if (getDevNull(&null) < 0)
+ goto cleanup;
+ infd = null;
+ }
+
+ if (outfd != NULL) {
+ if (*outfd == -1) {
+ if (pipe2(pipeout, O_CLOEXEC) < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot create pipe"));
+ goto cleanup;
+ }
+
+ if ((flags & VIR_EXEC_NONBLOCK) &&
+ virSetNonBlock(pipeout[0]) == -1) {
+ virReportSystemError(errno,
+ "%s", _("Failed to set non-blocking
file descriptor flag"));
+ goto cleanup;
+ }
+
+ childout = pipeout[1];
+ } else {
+ childout = *outfd;
+ }
+ } else {
+ if (getDevNull(&null) < 0)
+ goto cleanup;
+ childout = null;
+ }
+
+ if (errfd != NULL) {
+ if (errfd == outfd) {
+ childerr = childout;
+ } else if (*errfd == -1) {
+ if (pipe2(pipeerr, O_CLOEXEC) < 0) {
+ virReportSystemError(errno,
+ "%s", _("Failed to create
pipe"));
+ goto cleanup;
+ }
+
+ if ((flags & VIR_EXEC_NONBLOCK) &&
+ virSetNonBlock(pipeerr[0]) == -1) {
+ virReportSystemError(errno,
+ "%s", _("Failed to set non-blocking
file descriptor flag"));
+ goto cleanup;
+ }
+
+ childerr = pipeerr[1];
+ } else {
+ childerr = *errfd;
+ }
+ } else {
+ if (getDevNull(&null) < 0)
+ goto cleanup;
+ childerr = null;
+ }
+
+ forkRet = virFork(&pid);
+
+ if (pid < 0) {
+ goto cleanup;
+ }
+
+ if (pid) { /* parent */
+ if (forkRet < 0) {
+ goto cleanup;
+ }
+
+ VIR_FORCE_CLOSE(null);
+ if (outfd && *outfd == -1) {
+ VIR_FORCE_CLOSE(pipeout[1]);
+ *outfd = pipeout[0];
+ }
+ if (errfd && *errfd == -1) {
+ VIR_FORCE_CLOSE(pipeerr[1]);
+ *errfd = pipeerr[0];
+ }
+
+ *retpid = pid;
+
+ if (binary != argv[0])
+ VIR_FREE(binary);
+
+ return 0;
+ }
+
+ /* child */
+
+ if (forkRet < 0) {
+ /* The fork was successful, but after that there was an error
+ * in the child (which was already logged).
+ */
+ goto fork_error;
+ }
+
+ openmax = sysconf(_SC_OPEN_MAX);
+ for (i = 3; i < openmax; i++) {
+ if (i == infd || i == childout || i == childerr)
+ continue;
+ if (!keepfd || !virCommandFDIsSet(i, keepfd, keepfd_size)) {
+ tmpfd = i;
+ VIR_MASS_CLOSE(tmpfd);
+ } else if (virSetInherit(i, true) < 0) {
+ virReportSystemError(errno, _("failed to preserve fd %d"), i);
+ goto fork_error;
+ }
+ }
+
+ if (prepareStdFd(infd, STDIN_FILENO) < 0) {
+ virReportSystemError(errno,
+ "%s", _("failed to setup stdin file
handle"));
+ goto fork_error;
+ }
+ if (childout > 0 && prepareStdFd(childout, STDOUT_FILENO) < 0) {
+ virReportSystemError(errno,
+ "%s", _("failed to setup stdout file
handle"));
+ goto fork_error;
+ }
+ if (childerr > 0 && prepareStdFd(childerr, STDERR_FILENO) < 0) {
+ virReportSystemError(errno,
+ "%s", _("failed to setup stderr file
handle"));
+ goto fork_error;
+ }
+
+ if (infd != STDIN_FILENO && infd != null && infd != childerr
&&
+ infd != childout)
+ VIR_FORCE_CLOSE(infd);
+ if (childout > STDERR_FILENO && childout != null && childout !=
childerr)
+ VIR_FORCE_CLOSE(childout);
+ if (childerr > STDERR_FILENO && childerr != null)
+ VIR_FORCE_CLOSE(childerr);
+ VIR_FORCE_CLOSE(null);
+
+ /* Initialize full logging for a while */
+ virLogSetFromEnv();
+
+ /* Daemonize as late as possible, so the parent process can detect
+ * the above errors with wait* */
+ if (flags & VIR_EXEC_DAEMON) {
+ if (setsid() < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot become session
leader"));
+ goto fork_error;
+ }
+
+ if (chdir("/") < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot change to root
directory"));
+ goto fork_error;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot fork child
process"));
+ goto fork_error;
+ }
+
+ if (pid > 0) {
+ if (pidfile && (virPidFileWritePath(pidfile,pid) < 0)) {
+ kill(pid, SIGTERM);
+ usleep(500*1000);
+ kill(pid, SIGTERM);
+ virReportSystemError(errno,
+ _("could not write pidfile %s for %d"),
+ pidfile, pid);
+ goto fork_error;
+ }
+ _exit(0);
+ }
+ }
+
+ if (hook) {
+ /* virFork reset all signal handlers to the defaults.
+ * This is good for the child process, but our hook
+ * risks running something that generates SIGPIPE,
+ * so we need to temporarily block that again
+ */
+ struct sigaction waxon, waxoff;
+ memset(&waxoff, 0, sizeof(waxoff));
+ waxoff.sa_handler = SIG_IGN;
+ sigemptyset(&waxoff.sa_mask);
+ memset(&waxon, 0, sizeof(waxon));
+ if (sigaction(SIGPIPE, &waxoff, &waxon) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Could not disable SIGPIPE"));
+ goto fork_error;
+ }
+
+ if ((hook)(data) != 0) {
+ VIR_DEBUG("Hook function failed.");
+ goto fork_error;
+ }
+
+ if (sigaction(SIGPIPE, &waxon, NULL) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Could not re-enable SIGPIPE"));
+ goto fork_error;
+ }
+ }
+
+ /* The steps above may need todo something privileged, so
+ * we delay clearing capabilities until the last minute */
+ if (capabilities || (flags & VIR_EXEC_CLEAR_CAPS))
+ if (virSetCapabilities(capabilities) < 0)
+ goto fork_error;
+
+ /* Close logging again to ensure no FDs leak to child */
+ virLogReset();
+
+ if (envp)
+ execve(binary, (char **) argv, (char**)envp);
+ else
+ execv(binary, (char **) argv);
+
+ virReportSystemError(errno,
+ _("cannot execute binary %s"),
+ argv[0]);
+
+ fork_error:
+ virDispatchError(NULL);
+ _exit(EXIT_FAILURE);
+
+ cleanup:
+ /* This is cleanup of parent process only - child
+ should never jump here on error */
+
+ if (binary != argv[0])
+ VIR_FREE(binary);
+
+ /* NB we don't virReportError() on any failures here
+ because the code which jumped here already raised
+ an error condition which we must not overwrite */
+ VIR_FORCE_CLOSE(pipeerr[0]);
+ VIR_FORCE_CLOSE(pipeerr[1]);
+ VIR_FORCE_CLOSE(pipeout[0]);
+ VIR_FORCE_CLOSE(pipeout[1]);
+ VIR_FORCE_CLOSE(null);
+ return -1;
+}
+
+/**
+ * virRun:
+ * @argv NULL terminated argv to run
+ * @status optional variable to return exit status in
+ *
+ * Run a command without using the shell.
+ *
+ * If status is NULL, then return 0 if the command run and
+ * exited with 0 status; Otherwise return -1
+ *
+ * If status is not-NULL, then return 0 if the command ran.
+ * The status variable is filled with the command exit status
+ * and should be checked by caller for success. Return -1
+ * only if the command could not be run.
+ */
+int
+virRun(const char *const*argv, int *status)
+{
+ int ret;
+ virCommandPtr cmd = virCommandNewArgs(argv);
+
+ ret = virCommandRun(cmd, status);
+ virCommandFree(cmd);
+ return ret;
+}
+
+#else /* WIN32 */
+
+int
+virRun(const char *const *argv ATTRIBUTE_UNUSED,
+ int *status)
+{
+ if (status)
+ *status = ENOTSUP;
+ else
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("virRun is not implemented for
WIN32"));
+ return -1;
+}
+
+static int
+virExecWithHook(const char *const*argv ATTRIBUTE_UNUSED,
+ const char *const*envp ATTRIBUTE_UNUSED,
+ const int *keepfd ATTRIBUTE_UNUSED,
+ int keepfd_size ATTRIBUTE_UNUSED,
+ pid_t *retpid ATTRIBUTE_UNUSED,
+ int infd ATTRIBUTE_UNUSED,
+ int *outfd ATTRIBUTE_UNUSED,
+ int *errfd ATTRIBUTE_UNUSED,
+ int flags_unused ATTRIBUTE_UNUSED,
+ virExecHook hook ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED,
+ char *pidfile ATTRIBUTE_UNUSED,
+ unsigned long long capabilities ATTRIBUTE_UNUSED)
+{
+ /* XXX: Some day we can implement pieces of virCommand/virExec on
+ * top of _spawn() or CreateProcess(), but we can't implement
+ * everything, since mingw completely lacks fork(), so we cannot
+ * run hook code in the child. */
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("virExec is not implemented for WIN32"));
+ return -1;
+}
+
+int
+virFork(pid_t *pid)
+{
+ *pid = -1;
+ errno = ENOTSUP;
+
+ return -1;
+}
+
+#endif /* WIN32 */
+
+
+/**
+ * virCommandNew:
+ * @binary: program to run
+ *
+ * Create a new command for named binary. If @binary is relative,
+ * it will be found via a PATH search of the parent's PATH (and not
+ * any altered PATH set by virCommandAddEnv* commands).
+ */
+virCommandPtr
+virCommandNew(const char *binary)
+{
+ const char *const args[] = { binary, NULL };
+
+ return virCommandNewArgs(args);
+}
+
+/**
+ * virCommandNewArgs:
+ * @args: array of arguments
+ *
+ * Create a new command with a NULL terminated
+ * set of args, taking binary from args[0]. More arguments can
+ * be added later. @args[0] is handled like @binary of virCommandNew.
+ */
+virCommandPtr
+virCommandNewArgs(const char *const*args)
+{
+ virCommandPtr cmd;
+
+ if (VIR_ALLOC(cmd) < 0)
+ return NULL;
+
+ cmd->handshakeWait[0] = -1;
+ cmd->handshakeWait[1] = -1;
+ cmd->handshakeNotify[0] = -1;
+ cmd->handshakeNotify[1] = -1;
+
+ cmd->infd = cmd->outfd = cmd->errfd = -1;
+ cmd->pid = -1;
+
+ virCommandAddArgSet(cmd, args);
+
+ return cmd;
+}
+
+/**
+ * virCommandNewArgList:
+ * @binary: program to run
+ * @...: additional arguments
+ *
+ * Create a new command with a NULL terminated
+ * list of args, starting with the binary to run. More arguments can
+ * be added later. @binary is handled as in virCommandNew.
+ */
+virCommandPtr
+virCommandNewArgList(const char *binary, ...)
+{
+ virCommandPtr cmd = virCommandNew(binary);
+ va_list list;
+ const char *arg;
+
+ if (!cmd || cmd->has_error)
+ return cmd;
+
+ va_start(list, binary);
+ while ((arg = va_arg(list, const char *)) != NULL)
+ virCommandAddArg(cmd, arg);
+ va_end(list);
+ return cmd;
+}
+
+/**
+ * virCommandNewVAList:
+ * @binary: program to run
+ * @va_list: additional arguments
+ *
+ * Create a new command with a NULL terminated
+ * variable argument list. @binary is handled as in virCommandNew.
+ */
+virCommandPtr
+virCommandNewVAList(const char *binary, va_list list)
+{
+ virCommandPtr cmd = virCommandNew(binary);
+ const char *arg;
+
+ if (!cmd || cmd->has_error)
+ return cmd;
+
+ while ((arg = va_arg(list, const char *)) != NULL)
+ virCommandAddArg(cmd, arg);
+ return cmd;
+}
+
+
+/*
+ * Preserve the specified file descriptor in the child, instead of
+ * 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.
+ * Returns true if a transferring caller should close FD now, and
+ * false if the transfer is successfully recorded.
+ */
+static bool
+virCommandKeepFD(virCommandPtr cmd, int fd, bool transfer)
+{
+ int ret = 0;
+
+ if (!cmd)
+ return fd > STDERR_FILENO;
+
+ if (fd <= STDERR_FILENO ||
+ (ret = virCommandFDSet(fd, &cmd->preserve, &cmd->preserve_size))
||
+ (transfer && (ret = virCommandFDSet(fd, &cmd->transfer,
+ &cmd->transfer_size)))) {
+ if (!cmd->has_error)
+ cmd->has_error = ret ? ret : -1 ;
+ VIR_DEBUG("cannot preserve %d", fd);
+ return fd > STDERR_FILENO;
+ }
+
+ return false;
+}
+
+/**
+ * virCommandPreserveFD:
+ * @cmd: the command to modify
+ * @fd: fd to mark for inheritance into child
+ *
+ * Preserve the specified file descriptor
+ * in the child, instead of closing it on exec.
+ * The parent is still responsible for managing fd.
+ */
+void
+virCommandPreserveFD(virCommandPtr cmd, int fd)
+{
+ virCommandKeepFD(cmd, fd, false);
+}
+
+/**
+ * virCommandTransferFD:
+ * @cmd: the command to modify
+ * @fd: fd to reassign to the child
+ *
+ * Transfer the specified file descriptor
+ * to the child, instead of closing it on exec.
+ * The parent should no longer use fd, and the parent's copy will
+ * be automatically closed no later than during Run/RunAsync/Free.
+ */
+void
+virCommandTransferFD(virCommandPtr cmd, int fd)
+{
+ if (virCommandKeepFD(cmd, fd, true))
+ VIR_FORCE_CLOSE(fd);
+}
+
+
+/**
+ * virCommandSetPidFile:
+ * @cmd: the command to modify
+ * @pidfile: filename to use
+ *
+ * Save the child PID in a pidfile. The pidfile will be populated
+ * before the exec of the child.
+ */
+void
+virCommandSetPidFile(virCommandPtr cmd, const char *pidfile)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ VIR_FREE(cmd->pidfile);
+ if (!(cmd->pidfile = strdup(pidfile))) {
+ cmd->has_error = ENOMEM;
+ }
+}
+
+
+/**
+ * virCommandClearCaps:
+ * @cmd: the command to modify
+ *
+ * Remove all capabilities from the child, after any hooks have been run.
+ */
+void
+virCommandClearCaps(virCommandPtr cmd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ cmd->flags |= VIR_EXEC_CLEAR_CAPS;
+}
+
+/**
+ * virCommandAllowCap:
+ * @cmd: the command to modify
+ * @capability: what to allow
+ *
+ * Allow specific capabilities
+ */
+void
+virCommandAllowCap(virCommandPtr cmd,
+ int capability)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ cmd->capabilities |= (1ULL << capability);
+}
+
+
+
+/**
+ * virCommandDaemonize:
+ * @cmd: the command to modify
+ *
+ * Daemonize the child process. The child will have a current working
+ * directory of /, and must be started with virCommandRun, which will
+ * complete as soon as the daemon grandchild has started.
+ */
+void
+virCommandDaemonize(virCommandPtr cmd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ cmd->flags |= VIR_EXEC_DAEMON;
+}
+
+/**
+ * virCommandNonblockingFDs:
+ * @cmd: the command to modify
+ *
+ * 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 cmd->env list. 'env' is a
+ * string like "name=value". If the named environment variable is
+ * already set, then it is replaced in the list.
+ */
+static inline void
+virCommandAddEnv(virCommandPtr cmd, char *env)
+{
+ size_t namelen;
+ size_t i;
+
+ /* Search for the name in the existing environment. */
+ namelen = strcspn(env, "=");
+ for (i = 0; i < cmd->nenv; ++i) {
+ /* + 1 because we want to match the '=' character too. */
+ if (STREQLEN(cmd->env[i], env, namelen + 1)) {
+ VIR_FREE(cmd->env[i]);
+ cmd->env[i] = env;
+ return;
+ }
+ }
+
+ /* Arg plus trailing NULL. */
+ if (VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 1 + 1) < 0) {
+ VIR_FREE(env);
+ cmd->has_error = ENOMEM;
+ return;
+ }
+
+ cmd->env[cmd->nenv++] = env;
+}
+
+/**
+ * virCommandAddEnvFormat:
+ * @cmd: the command to modify
+ * @format: format of arguments, end result must be in name=value format
+ * @...: arguments to be formatted
+ *
+ * Add an environment variable to the child created by a printf-style format.
+ */
+void
+virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...)
+{
+ char *env;
+ va_list list;
+
+ if (!cmd || cmd->has_error)
+ return;
+
+ va_start(list, format);
+ if (virVasprintf(&env, format, list) < 0) {
+ cmd->has_error = ENOMEM;
+ va_end(list);
+ return;
+ }
+ va_end(list);
+
+ virCommandAddEnv(cmd, env);
+}
+
+/**
+ * virCommandAddEnvPair:
+ * @cmd: the command to modify
+ * @name: variable name, must not contain =
+ * @value: value to assign to name
+ *
+ * Add an environment variable to the child
+ * using separate name & value strings
+ */
+void
+virCommandAddEnvPair(virCommandPtr cmd, const char *name, const char *value)
+{
+ virCommandAddEnvFormat(cmd, "%s=%s", name, value);
+}
+
+
+/**
+ * virCommandAddEnvString:
+ * @cmd: the command to modify
+ * @str: name=value format
+ *
+ * Add an environment variable to the child
+ * using a preformatted env string FOO=BAR
+ */
+void
+virCommandAddEnvString(virCommandPtr cmd, const char *str)
+{
+ char *env;
+
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (!(env = strdup(str))) {
+ cmd->has_error = ENOMEM;
+ return;
+ }
+
+ virCommandAddEnv(cmd, env);
+}
+
+
+/**
+ * virCommandAddEnvBuffer:
+ * @cmd: the command to modify
+ * @buf: buffer that contains name=value string, which will be reset on return
+ *
+ * Convert a buffer containing preformatted name=value into an
+ * environment variable of the child.
+ * Correctly transfers memory errors or contents from buf to cmd.
+ */
+void
+virCommandAddEnvBuffer(virCommandPtr cmd, virBufferPtr buf)
+{
+ if (!cmd || cmd->has_error) {
+ virBufferFreeAndReset(buf);
+ return;
+ }
+
+ if (virBufferError(buf)) {
+ cmd->has_error = ENOMEM;
+ virBufferFreeAndReset(buf);
+ return;
+ }
+ if (!virBufferUse(buf)) {
+ cmd->has_error = EINVAL;
+ return;
+ }
+
+ virCommandAddEnv(cmd, virBufferContentAndReset(buf));
+}
+
+
+/**
+ * virCommandAddEnvPass:
+ * @cmd: the command to modify
+ * @name: the name to look up in current environment
+ *
+ * Pass an environment variable to the child
+ * using current process' value
+ */
+void
+virCommandAddEnvPass(virCommandPtr cmd, const char *name)
+{
+ char *value;
+ if (!cmd || cmd->has_error)
+ return;
+
+ value = getenv(name);
+ if (value)
+ virCommandAddEnvPair(cmd, name, value);
+}
+
+
+/**
+ * virCommandAddEnvPassCommon:
+ * @cmd: the command to modify
+ *
+ * Set LC_ALL to C, and propagate other essential environment
+ * variables (such as PATH) from the parent process.
+ */
+void
+virCommandAddEnvPassCommon(virCommandPtr cmd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ /* Attempt to Pre-allocate; allocation failure will be detected
+ * later during virCommandAdd*. */
+ ignore_value(VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 9));
+
+ virCommandAddEnvPair(cmd, "LC_ALL", "C");
+
+ virCommandAddEnvPass(cmd, "LD_PRELOAD");
+ virCommandAddEnvPass(cmd, "LD_LIBRARY_PATH");
+ virCommandAddEnvPass(cmd, "PATH");
+ virCommandAddEnvPass(cmd, "HOME");
+ virCommandAddEnvPass(cmd, "USER");
+ virCommandAddEnvPass(cmd, "LOGNAME");
+ virCommandAddEnvPass(cmd, "TMPDIR");
+}
+
+/**
+ * virCommandAddArg:
+ * @cmd: the command to modify
+ * @val: the argument to add
+ *
+ * Add a command line argument to the child
+ */
+void
+virCommandAddArg(virCommandPtr cmd, const char *val)
+{
+ char *arg;
+
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (!(arg = strdup(val))) {
+ cmd->has_error = ENOMEM;
+ return;
+ }
+
+ /* Arg plus trailing NULL. */
+ if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) {
+ VIR_FREE(arg);
+ cmd->has_error = ENOMEM;
+ return;
+ }
+
+ cmd->args[cmd->nargs++] = arg;
+}
+
+
+/**
+ * virCommandAddArgBuffer:
+ * @cmd: the command to modify
+ * @buf: buffer that contains argument string, which will be reset on return
+ *
+ * Convert a buffer into a command line argument to the child.
+ * Correctly transfers memory errors or contents from buf to cmd.
+ */
+void
+virCommandAddArgBuffer(virCommandPtr cmd, virBufferPtr buf)
+{
+ if (!cmd || cmd->has_error) {
+ virBufferFreeAndReset(buf);
+ return;
+ }
+
+ /* Arg plus trailing NULL. */
+ if (virBufferError(buf) ||
+ VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) {
+ cmd->has_error = ENOMEM;
+ virBufferFreeAndReset(buf);
+ return;
+ }
+
+ cmd->args[cmd->nargs] = virBufferContentAndReset(buf);
+ if (!cmd->args[cmd->nargs])
+ cmd->args[cmd->nargs] = strdup("");
+ if (!cmd->args[cmd->nargs]) {
+ cmd->has_error = ENOMEM;
+ return;
+ }
+ cmd->nargs++;
+}
+
+
+/**
+ * virCommandAddArgFormat:
+ * @cmd: the command to modify
+ * @format: format of arguments, end result must be in name=value format
+ * @...: arguments to be formatted
+ *
+ * Add a command line argument created by a printf-style format.
+ */
+void
+virCommandAddArgFormat(virCommandPtr cmd, const char *format, ...)
+{
+ char *arg;
+ va_list list;
+
+ if (!cmd || cmd->has_error)
+ return;
+
+ 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) {
+ VIR_FREE(arg);
+ cmd->has_error = ENOMEM;
+ return;
+ }
+
+ cmd->args[cmd->nargs++] = arg;
+}
+
+/**
+ * virCommandAddArgPair:
+ * @cmd: the command to modify
+ * @name: left half of argument
+ * @value: right half of argument
+ *
+ * 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);
+}
+
+/**
+ * virCommandAddArgSet:
+ * @cmd: the command to modify
+ * @vals: array of arguments to add
+ *
+ * Add a NULL terminated list of args
+ */
+void
+virCommandAddArgSet(virCommandPtr cmd, const char *const*vals)
+{
+ int narg = 0;
+
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (vals[0] == NULL) {
+ cmd->has_error = EINVAL;
+ return;
+ }
+
+ while (vals[narg] != NULL)
+ narg++;
+
+ /* narg plus trailing NULL. */
+ if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1) < 0) {
+ cmd->has_error = ENOMEM;
+ return;
+ }
+
+ narg = 0;
+ while (vals[narg] != NULL) {
+ char *arg = strdup(vals[narg++]);
+ if (!arg) {
+ cmd->has_error = ENOMEM;
+ return;
+ }
+ cmd->args[cmd->nargs++] = arg;
+ }
+}
+
+/**
+ * virCommandAddArgList:
+ * @cmd: the command to modify
+ * @...: list of arguments to add
+ *
+ * Add a NULL terminated list of args.
+ */
+void
+virCommandAddArgList(virCommandPtr cmd, ...)
+{
+ va_list list;
+ int narg = 0;
+
+ if (!cmd || cmd->has_error)
+ return;
+
+ va_start(list, cmd);
+ while (va_arg(list, const char *) != NULL)
+ narg++;
+ va_end(list);
+
+ /* narg plus trailing NULL. */
+ if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1) < 0) {
+ cmd->has_error = ENOMEM;
+ return;
+ }
+
+ va_start(list, cmd);
+ while (1) {
+ char *arg = va_arg(list, char *);
+ if (!arg)
+ break;
+ arg = strdup(arg);
+ if (!arg) {
+ cmd->has_error = ENOMEM;
+ va_end(list);
+ return;
+ }
+ cmd->args[cmd->nargs++] = arg;
+ }
+ va_end(list);
+}
+
+/**
+ * virCommandSetWorkingDirectory:
+ * @cmd: the command to modify
+ * @pwd: directory to use
+ *
+ * Set the working directory of a non-daemon child process, rather
+ * than the parent's working directory. Daemons automatically get /
+ * without using this call.
+ */
+void
+virCommandSetWorkingDirectory(virCommandPtr cmd, const char *pwd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->pwd) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot set directory twice");
+ } else {
+ cmd->pwd = strdup(pwd);
+ if (!cmd->pwd)
+ cmd->has_error = ENOMEM;
+ }
+}
+
+
+/**
+ * virCommandSetInputBuffer:
+ * @cmd: the command to modify
+ * @inbuf: string to feed to stdin
+ *
+ * Feed the child's stdin from a string buffer. This requires the use
+ * of virCommandRun().
+ */
+void
+virCommandSetInputBuffer(virCommandPtr cmd, const char *inbuf)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->infd != -1 || cmd->inbuf) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify input twice");
+ return;
+ }
+
+ cmd->inbuf = strdup(inbuf);
+ if (!cmd->inbuf)
+ cmd->has_error = ENOMEM;
+}
+
+
+/**
+ * virCommandSetOutputBuffer:
+ * @cmd: the command to modify
+ * @outbuf: address of variable to store malloced result buffer
+ *
+ * Capture the child's stdout to a string buffer. *outbuf is
+ * guaranteed to be allocated after successful virCommandRun or
+ * virCommandWait, and is best-effort allocated after failed
+ * virCommandRun; caller is responsible for freeing *outbuf.
+ * This requires the use of virCommandRun.
+ */
+void
+virCommandSetOutputBuffer(virCommandPtr cmd, char **outbuf)
+{
+ *outbuf = NULL;
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->outfdptr) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify output twice");
+ return;
+ }
+
+ cmd->outbuf = outbuf;
+ cmd->outfdptr = &cmd->outfd;
+}
+
+
+/**
+ * virCommandSetErrorBuffer:
+ * @cmd: the command to modify
+ * @errbuf: address of variable to store malloced result buffer
+ *
+ * Capture the child's stderr to a string buffer. *errbuf is
+ * guaranteed to be allocated after successful virCommandRun or
+ * virCommandWait, and is best-effort allocated after failed
+ * virCommandRun; caller is responsible for freeing *errbuf.
+ * This requires the use of virCommandRun. It is possible to
+ * pass the same pointer as for virCommandSetOutputBuffer(), in
+ * which case the child process will interleave all output into
+ * a single string.
+ */
+void
+virCommandSetErrorBuffer(virCommandPtr cmd, char **errbuf)
+{
+ *errbuf = NULL;
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->errfdptr) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify stderr twice");
+ return;
+ }
+
+ cmd->errbuf = errbuf;
+ cmd->errfdptr = &cmd->errfd;
+}
+
+
+/**
+ * virCommandSetInputFD:
+ * @cmd: the command to modify
+ * @infd: the descriptor to use
+ *
+ * Attach a file descriptor to the child's stdin
+ */
+void
+virCommandSetInputFD(virCommandPtr cmd, int infd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->infd != -1 || cmd->inbuf) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify input twice");
+ return;
+ }
+ if (infd < 0) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify invalid input fd");
+ return;
+ }
+
+ cmd->infd = infd;
+}
+
+
+/**
+ * virCommandSetOutputFD:
+ * @cmd: the command to modify
+ * @outfd: location of output fd
+ *
+ * Attach a file descriptor to the child's stdout. If *@outfd is -1 on
+ * entry, then a pipe will be created and returned in this variable when
+ * the child is run. Otherwise, *@outfd is used as the output.
+ */
+void
+virCommandSetOutputFD(virCommandPtr cmd, int *outfd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->outfdptr) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify output twice");
+ return;
+ }
+
+ cmd->outfdptr = outfd;
+}
+
+
+/**
+ * virCommandSetErrorFD:
+ * @cmd: the command to modify
+ * @errfd: location of error fd
+ *
+ * Attach a file descriptor to the child's stderr. If *@errfd is -1 on
+ * entry, then a pipe will be created and returned in this variable when
+ * the child is run. Otherwise, *@errfd is used for error collection,
+ * and may be the same as outfd given to virCommandSetOutputFD().
+ */
+void
+virCommandSetErrorFD(virCommandPtr cmd, int *errfd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->errfdptr) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify stderr twice");
+ return;
+ }
+
+ cmd->errfdptr = errfd;
+}
+
+
+/**
+ * virCommandSetPreExecHook:
+ * @cmd: the command to modify
+ * @hook: the hook to run
+ * @opaque: argument to pass to the hook
+ *
+ * Run HOOK(OPAQUE) in the child as the last thing before changing
+ * directories, dropping capabilities, and executing the new process.
+ * Force the child to fail if HOOK does not return zero.
+ *
+ * Since @hook runs in the child, it should be careful to avoid
+ * any functions that are not async-signal-safe.
+ */
+void
+virCommandSetPreExecHook(virCommandPtr cmd, virExecHook hook, void *opaque)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->hook) {
+ cmd->has_error = -1;
+ VIR_DEBUG("cannot specify hook twice");
+ return;
+ }
+ cmd->hook = hook;
+ cmd->opaque = opaque;
+}
+
+
+/**
+ * virCommandWriteArgLog:
+ * @cmd: the command to log
+ * @logfd: where to log the results
+ *
+ * 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)));
+ }
+}
+
+
+/**
+ * virCommandToString:
+ * @cmd: the command to convert
+ *
+ * Call after adding all arguments and environment settings, but
+ * before Run/RunAsync, to return a string representation of the
+ * environment and arguments of cmd, suitably quoted for pasting into
+ * a shell. 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 == ENOMEM) {
+ virReportOOMError();
+ return NULL;
+ }
+ if (cmd->has_error) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return NULL;
+ }
+
+ for (i = 0; i < cmd->nenv; i++) {
+ /* In shell, a='b c' has a different meaning than 'a=b c', so
+ * we must determine where the '=' lives. */
+ char *eq = strchr(cmd->env[i], '=');
+
+ if (!eq) {
+ virBufferFreeAndReset(&buf);
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return NULL;
+ }
+ eq++;
+ virBufferAdd(&buf, cmd->env[i], eq - cmd->env[i]);
+ virBufferEscapeShell(&buf, eq);
+ virBufferAddChar(&buf, ' ');
+ }
+ virBufferEscapeShell(&buf, cmd->args[0]);
+ for (i = 1; i < cmd->nargs; i++) {
+ virBufferAddChar(&buf, ' ');
+ virBufferEscapeShell(&buf, cmd->args[i]);
+ }
+
+ if (virBufferError(&buf)) {
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return NULL;
+ }
+
+ return virBufferContentAndReset(&buf);
+}
+
+
+/*
+ * Manage input and output to the child process.
+ */
+static int
+virCommandProcessIO(virCommandPtr cmd, int *inpipe)
+{
+ int infd = -1, outfd = -1, errfd = -1;
+ size_t inlen = 0, outlen = 0, errlen = 0;
+ size_t inoff = 0;
+ int ret = 0;
+
+ /* With an input buffer, feed data to child
+ * via pipe */
+ if (cmd->inbuf) {
+ inlen = strlen(cmd->inbuf);
+ infd = *inpipe;
+ }
+
+ /* With out/err buffer, the outfd/errfd have been filled with an
+ * FD for us. Guarantee an allocated string with partial results
+ * even if we encounter a later failure, as well as freeing any
+ * results accumulated over a prior run of the same command. */
+ if (cmd->outbuf) {
+ outfd = cmd->outfd;
+ if (VIR_REALLOC_N(*cmd->outbuf, 1) < 0) {
+ virReportOOMError();
+ ret = -1;
+ }
+ }
+ if (cmd->errbuf) {
+ errfd = cmd->errfd;
+ if (VIR_REALLOC_N(*cmd->errbuf, 1) < 0) {
+ virReportOOMError();
+ ret = -1;
+ }
+ }
+ if (ret == -1)
+ goto cleanup;
+ ret = -1;
+
+ for (;;) {
+ int i;
+ struct pollfd fds[3];
+ int nfds = 0;
+
+ if (infd != -1) {
+ fds[nfds].fd = infd;
+ fds[nfds].events = POLLOUT;
+ fds[nfds].revents = 0;
+ nfds++;
+ }
+ if (outfd != -1) {
+ fds[nfds].fd = outfd;
+ fds[nfds].events = POLLIN;
+ fds[nfds].revents = 0;
+ nfds++;
+ }
+ if (errfd != -1) {
+ fds[nfds].fd = errfd;
+ fds[nfds].events = POLLIN;
+ fds[nfds].revents = 0;
+ nfds++;
+ }
+
+ if (nfds == 0)
+ break;
+
+ if (poll(fds, nfds, -1) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("unable to poll on child"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < nfds ; i++) {
+ if (fds[i].revents & (POLLIN | POLLHUP | POLLERR) &&
+ (fds[i].fd == errfd || fds[i].fd == outfd)) {
+ char data[1024];
+ char **buf;
+ size_t *len;
+ int done;
+ if (fds[i].fd == outfd) {
+ buf = cmd->outbuf;
+ len = &outlen;
+ } else {
+ buf = cmd->errbuf;
+ len = &errlen;
+ }
+ /* Silence a false positive from clang. */
+ sa_assert(buf);
+
+ done = read(fds[i].fd, data, sizeof(data));
+ if (done < 0) {
+ if (errno != EINTR &&
+ errno != EAGAIN) {
+ virReportSystemError(errno, "%s",
+ (fds[i].fd == outfd) ?
+ _("unable to read child stdout")
:
+ _("unable to read child
stderr"));
+ goto cleanup;
+ }
+ } else if (done == 0) {
+ if (fds[i].fd == outfd)
+ outfd = -1;
+ else
+ errfd = -1;
+ } else {
+ if (VIR_REALLOC_N(*buf, *len + done + 1) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ memcpy(*buf + *len, data, done);
+ *len += done;
+ }
+ }
+
+ if (fds[i].revents & (POLLOUT | POLLERR) &&
+ fds[i].fd == infd) {
+ int done;
+
+ /* Coverity 5.3.0 can't see that we only get here if
+ * infd is in the set because it was non-negative. */
+ sa_assert(infd != -1);
+ done = write(infd, cmd->inbuf + inoff,
+ inlen - inoff);
+ if (done < 0) {
+ if (errno == EPIPE) {
+ VIR_DEBUG("child closed stdin early, ignoring EPIPE "
+ "on fd %d", infd);
+ if (VIR_CLOSE(*inpipe) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", infd);
+ infd = -1;
+ } else if (errno != EINTR && errno != EAGAIN) {
+ virReportSystemError(errno, "%s",
+ _("unable to write to child
input"));
+ goto cleanup;
+ }
+ } else {
+ inoff += done;
+ if (inoff == inlen) {
+ if (VIR_CLOSE(*inpipe) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", infd);
+ infd = -1;
+ }
+ }
+ }
+ }
+ }
+
+ ret = 0;
+cleanup:
+ if (cmd->outbuf && *cmd->outbuf)
+ (*cmd->outbuf)[outlen] = '\0';
+ if (cmd->errbuf && *cmd->errbuf)
+ (*cmd->errbuf)[errlen] = '\0';
+ return ret;
+}
+
+/**
+ * virCommandExec:
+ * @cmd: command to run
+ *
+ * Exec the command, replacing the current process. Meant to be called
+ * in the hook after already forking / cloning, so does not attempt to
+ * daemonize or preserve any FDs.
+ *
+ * Returns -1 on any error executing the command.
+ * Will not return on success.
+ */
+#ifndef WIN32
+int virCommandExec(virCommandPtr cmd)
+{
+ if (!cmd ||cmd->has_error == ENOMEM) {
+ virReportOOMError();
+ return -1;
+ }
+ if (cmd->has_error) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return -1;
+ }
+
+ return execve(cmd->args[0], cmd->args, cmd->env);
+}
+#else
+int virCommandExec(virCommandPtr cmd ATTRIBUTE_UNUSED)
+{
+ /* Mingw execve() has a broken signature. Disable this
+ * function until gnulib fixes the signature, since we
+ * don't really need this on Win32 anyway.
+ */
+ virReportSystemError(ENOSYS, "%s",
+ _("Executing new processes is not supported on Win32
platform"));
+ return -1;
+}
+#endif
+
+/**
+ * virCommandRun:
+ * @cmd: command to run
+ * @exitstatus: optional status collection
+ *
+ * Run the command and wait for completion.
+ * Returns -1 on any error executing the
+ * command. Returns 0 if the command executed,
+ * with the exit status set. If @exitstatus is NULL, then the
+ * child must exit with status 0 for this to succeed.
+ */
+int
+virCommandRun(virCommandPtr cmd, int *exitstatus)
+{
+ int ret = 0;
+ char *outbuf = NULL;
+ char *errbuf = NULL;
+ int infd[2] = { -1, -1 };
+ struct stat st;
+ bool string_io;
+ bool async_io = false;
+ char *str;
+ int tmpfd;
+
+ if (!cmd ||cmd->has_error == ENOMEM) {
+ virReportOOMError();
+ return -1;
+ }
+ if (cmd->has_error) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return -1;
+ }
+
+ /* Avoid deadlock, by requiring that any open fd not under our
+ * control must be visiting a regular file, or that we are
+ * daemonized and no string io is required. */
+ string_io = cmd->inbuf || cmd->outbuf || cmd->errbuf;
+ if (cmd->infd != -1 &&
+ (fstat(cmd->infd, &st) < 0 || !S_ISREG(st.st_mode)))
+ async_io = true;
+ if (cmd->outfdptr && cmd->outfdptr != &cmd->outfd &&
+ (*cmd->outfdptr == -1 ||
+ fstat(*cmd->outfdptr, &st) < 0 || !S_ISREG(st.st_mode)))
+ async_io = true;
+ if (cmd->errfdptr && cmd->errfdptr != &cmd->errfd &&
+ (*cmd->errfdptr == -1 ||
+ fstat(*cmd->errfdptr, &st) < 0 || !S_ISREG(st.st_mode)))
+ async_io = true;
+ if (async_io) {
+ if (!(cmd->flags & VIR_EXEC_DAEMON) || string_io) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot mix caller fds with blocking
execution"));
+ return -1;
+ }
+ } else {
+ if ((cmd->flags & VIR_EXEC_DAEMON) && string_io) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot mix string I/O with daemon"));
+ return -1;
+ }
+ }
+
+ /* If we have an input buffer, we need
+ * a pipe to feed the data to the child */
+ if (cmd->inbuf) {
+ if (pipe2(infd, O_CLOEXEC) < 0) {
+ virReportSystemError(errno, "%s",
+ _("unable to open pipe"));
+ cmd->has_error = -1;
+ return -1;
+ }
+ cmd->infd = infd[0];
+ }
+
+ /* If caller requested the same string for stdout and stderr, then
+ * merge those into one string. */
+ if (cmd->outbuf && cmd->outbuf == cmd->errbuf) {
+ cmd->errfdptr = &cmd->outfd;
+ cmd->errbuf = NULL;
+ }
+
+ /* If caller hasn't requested capture of stdout/err, then capture
+ * it ourselves so we can log it. But the intermediate child for
+ * a daemon has no expected output, and we don't want our
+ * capturing pipes passed on to the daemon grandchild.
+ */
+ if (!(cmd->flags & VIR_EXEC_DAEMON)) {
+ if (!cmd->outfdptr) {
+ cmd->outfdptr = &cmd->outfd;
+ cmd->outbuf = &outbuf;
+ string_io = true;
+ }
+ if (!cmd->errfdptr) {
+ cmd->errfdptr = &cmd->errfd;
+ cmd->errbuf = &errbuf;
+ string_io = true;
+ }
+ }
+
+ cmd->flags |= VIR_EXEC_RUN_SYNC;
+ if (virCommandRunAsync(cmd, NULL) < 0) {
+ if (cmd->inbuf) {
+ tmpfd = infd[0];
+ if (VIR_CLOSE(infd[0]) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
+ tmpfd = infd[1];
+ if (VIR_CLOSE(infd[1]) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
+ }
+ cmd->has_error = -1;
+ return -1;
+ }
+
+ tmpfd = infd[0];
+ if (VIR_CLOSE(infd[0]) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
+ if (string_io)
+ ret = virCommandProcessIO(cmd, &infd[1]);
+
+ if (virCommandWait(cmd, exitstatus) < 0)
+ ret = -1;
+
+ str = (exitstatus ? virProcessTranslateStatus(*exitstatus)
+ : (char *) "status 0");
+ VIR_DEBUG("Result %s, stdout: '%s' stderr: '%s'",
+ NULLSTR(str),
+ cmd->outbuf ? NULLSTR(*cmd->outbuf) : "(null)",
+ cmd->errbuf ? NULLSTR(*cmd->errbuf) : "(null)");
+ if (exitstatus)
+ VIR_FREE(str);
+
+ /* Reset any capturing, in case caller runs
+ * this identical command again */
+ if (cmd->inbuf) {
+ tmpfd = infd[1];
+ if (VIR_CLOSE(infd[1]) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
+ }
+ if (cmd->outbuf == &outbuf) {
+ tmpfd = cmd->outfd;
+ if (VIR_CLOSE(cmd->outfd) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
+ cmd->outfdptr = NULL;
+ cmd->outbuf = NULL;
+ VIR_FREE(outbuf);
+ }
+ if (cmd->errbuf == &errbuf) {
+ tmpfd = cmd->errfd;
+ if (VIR_CLOSE(cmd->errfd) < 0)
+ VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
+ cmd->errfdptr = NULL;
+ cmd->errbuf = NULL;
+ VIR_FREE(errbuf);
+ }
+
+ return ret;
+}
+
+
+/*
+ * Perform all virCommand-specific actions, along with the user hook.
+ */
+static int
+virCommandHook(void *data)
+{
+ virCommandPtr cmd = data;
+ int res = 0;
+
+ if (cmd->hook) {
+ VIR_DEBUG("Run hook %p %p", cmd->hook, cmd->opaque);
+ res = cmd->hook(cmd->opaque);
+ VIR_DEBUG("Done hook %d", res);
+ }
+ if (res == 0 && cmd->pwd) {
+ VIR_DEBUG("Running child in %s", cmd->pwd);
+ res = chdir(cmd->pwd);
+ if (res < 0) {
+ virReportSystemError(errno,
+ _("Unable to change to %s"), cmd->pwd);
+ }
+ }
+ if (cmd->handshake) {
+ char c = res < 0 ? '0' : '1';
+ int rv;
+ VIR_DEBUG("Notifying parent for handshake start on %d",
+ cmd->handshakeWait[1]);
+ if (safewrite(cmd->handshakeWait[1], &c, sizeof(c)) != sizeof(c)) {
+ virReportSystemError(errno, "%s",
+ _("Unable to notify parent process"));
+ return -1;
+ }
+
+ /* On failure we pass the error message back to parent,
+ * so they don't have to dig through stderr logs
+ */
+ if (res < 0) {
+ virErrorPtr err = virGetLastError();
+ const char *msg = err ? err->message :
+ _("Unknown failure during hook execution");
+ size_t len = strlen(msg) + 1;
+ if (safewrite(cmd->handshakeWait[1], msg, len) != len) {
+ virReportSystemError(errno, "%s",
+ _("Unable to send error to parent"));
+ return -1;
+ }
+ return -1;
+ }
+
+ VIR_DEBUG("Waiting on parent for handshake complete on %d",
+ cmd->handshakeNotify[0]);
+ if ((rv = saferead(cmd->handshakeNotify[0], &c,
+ sizeof(c))) != sizeof(c)) {
+ if (rv < 0)
+ virReportSystemError(errno, "%s",
+ _("Unable to wait on parent process"));
+ else
+ virReportSystemError(EIO, "%s",
+ _("libvirtd quit during handshake"));
+ return -1;
+ }
+ if (c != '1') {
+ virReportSystemError(EINVAL,
+ _("Unexpected confirm code '%c' from
parent"),
+ c);
+ return -1;
+ }
+ VIR_FORCE_CLOSE(cmd->handshakeWait[1]);
+ VIR_FORCE_CLOSE(cmd->handshakeNotify[0]);
+ }
+
+ VIR_DEBUG("Hook is done %d", res);
+
+ return res;
+}
+
+
+/**
+ * virCommandRunAsync:
+ * @cmd: command to start
+ * @pid: optional variable to track child pid
+ *
+ * Run the command asynchronously
+ * Returns -1 on any error executing the
+ * command. Returns 0 if the command executed.
+ *
+ * There are two approaches to child process cleanup.
+ * 1. Use auto-cleanup, by passing NULL for pid. The child will be
+ * auto-reaped by virCommandFree, unless you reap it earlier via
+ * virCommandWait or virCommandAbort. Good for where cmd is in
+ * scope for the duration of the child process.
+ * 2. Use manual cleanup, by passing the address of a pid_t variable
+ * for pid. While cmd is still in scope, you may reap the child via
+ * virCommandWait or virCommandAbort. But after virCommandFree, if
+ * you have not yet reaped the child, then it continues to run until
+ * you call virProcessWait or virProcessAbort.
+ */
+int
+virCommandRunAsync(virCommandPtr cmd, pid_t *pid)
+{
+ int ret;
+ char *str;
+ int i;
+ bool synchronous = false;
+
+ if (!cmd || cmd->has_error == ENOMEM) {
+ virReportOOMError();
+ return -1;
+ }
+ if (cmd->has_error) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return -1;
+ }
+
+ synchronous = cmd->flags & VIR_EXEC_RUN_SYNC;
+ cmd->flags &= ~VIR_EXEC_RUN_SYNC;
+
+ /* Buffer management can only be requested via virCommandRun. */
+ if ((cmd->inbuf && cmd->infd == -1) ||
+ (cmd->outbuf && cmd->outfdptr != &cmd->outfd) ||
+ (cmd->errbuf && cmd->errfdptr != &cmd->errfd)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot mix string I/O with asynchronous command"));
+ return -1;
+ }
+
+ if (cmd->pid != -1) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("command is already running as pid %lld"),
+ (long long) cmd->pid);
+ return -1;
+ }
+
+ if (!synchronous && (cmd->flags & VIR_EXEC_DAEMON)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("daemonized command cannot use virCommandRunAsync"));
+ return -1;
+ }
+ if (cmd->pwd && (cmd->flags & VIR_EXEC_DAEMON)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("daemonized command cannot set working directory
%s"),
+ cmd->pwd);
+ return -1;
+ }
+ if (cmd->pidfile && !(cmd->flags & VIR_EXEC_DAEMON)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("creation of pid file requires daemonized command"));
+ return -1;
+ }
+
+ str = virCommandToString(cmd);
+ VIR_DEBUG("About to run %s", str ? str : cmd->args[0]);
+ VIR_FREE(str);
+
+ ret = virExecWithHook((const char *const *)cmd->args,
+ (const char *const *)cmd->env,
+ cmd->preserve,
+ cmd->preserve_size,
+ &cmd->pid,
+ cmd->infd,
+ cmd->outfdptr,
+ cmd->errfdptr,
+ cmd->flags,
+ virCommandHook,
+ cmd,
+ cmd->pidfile,
+ cmd->capabilities);
+
+ VIR_DEBUG("Command result %d, with PID %d",
+ ret, (int)cmd->pid);
+
+ for (i = 0; i < cmd->transfer_size; i++) {
+ VIR_FORCE_CLOSE(cmd->transfer[i]);
+ }
+ cmd->transfer_size = 0;
+ VIR_FREE(cmd->transfer);
+
+ if (ret == 0 && pid)
+ *pid = cmd->pid;
+ else
+ cmd->reap = true;
+
+ return ret;
+}
+
+
+/**
+ * virCommandWait:
+ * @cmd: command to wait on
+ * @exitstatus: optional status collection
+ *
+ * Wait for the command previously started with virCommandRunAsync()
+ * to complete. Return -1 on any error waiting for
+ * completion. Returns 0 if the command
+ * finished with the exit status set. If @exitstatus is NULL, then the
+ * child must exit with status 0 for this to succeed.
+ */
+int
+virCommandWait(virCommandPtr cmd, int *exitstatus)
+{
+ int ret;
+ int status = 0;
+
+ if (!cmd ||cmd->has_error == ENOMEM) {
+ virReportOOMError();
+ return -1;
+ }
+ if (cmd->has_error) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return -1;
+ }
+
+ if (cmd->pid == -1) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("command is not yet running"));
+ return -1;
+ }
+
+ /* If virProcessWait reaps pid but then returns failure because
+ * exitstatus was NULL, then a second virCommandWait would risk
+ * calling waitpid on an unrelated process. Besides, that error
+ * message is not as detailed as what we can provide. So, we
+ * guarantee that virProcessWait only fails due to failure to wait,
+ * and repeat the exitstatus check code ourselves. */
+ ret = virProcessWait(cmd->pid, exitstatus ? exitstatus : &status);
+ if (ret == 0) {
+ cmd->pid = -1;
+ cmd->reap = false;
+ if (status) {
+ char *str = virCommandToString(cmd);
+ char *st = virProcessTranslateStatus(status);
+ bool haveErrMsg = cmd->errbuf && *cmd->errbuf &&
(*cmd->errbuf)[0];
+
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Child process (%s) unexpected %s%s%s"),
+ str ? str : cmd->args[0], NULLSTR(st),
+ haveErrMsg ? ": " : "",
+ haveErrMsg ? *cmd->errbuf : "");
+ VIR_FREE(str);
+ VIR_FREE(st);
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+
+#ifndef WIN32
+/**
+ * virCommandAbort:
+ * @cmd: command to abort
+ *
+ * Abort an async command if it is running, without issuing
+ * any errors or affecting errno. Designed for error paths
+ * where some but not all paths to the cleanup code might
+ * have started the child process.
+ */
+void
+virCommandAbort(virCommandPtr cmd)
+{
+ if (!cmd || cmd->pid == -1)
+ return;
+ virProcessAbort(cmd->pid);
+ cmd->pid = -1;
+ cmd->reap = false;
+}
+#else /* WIN32 */
+void
+virCommandAbort(virCommandPtr cmd ATTRIBUTE_UNUSED)
+{
+ /* Mingw lacks WNOHANG and kill(). But since we haven't ported
+ * virExecWithHook to mingw yet, there's no process to be killed,
+ * making this implementation trivially correct for now :) */
+}
+#endif
+
+
+/**
+ * virCommandRequireHandshake:
+ * @cmd: command to modify
+ *
+ * Request that the child perform a handshake with
+ * the parent when the hook function has completed
+ * execution. The child will not exec() until the
+ * parent has notified
+ */
+void virCommandRequireHandshake(virCommandPtr cmd)
+{
+ if (!cmd || cmd->has_error)
+ return;
+
+ if (cmd->handshake) {
+ cmd->has_error = -1;
+ VIR_DEBUG("Cannot require handshake twice");
+ return;
+ }
+
+ if (pipe2(cmd->handshakeWait, O_CLOEXEC) < 0) {
+ cmd->has_error = errno;
+ return;
+ }
+ if (pipe2(cmd->handshakeNotify, O_CLOEXEC) < 0) {
+ VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
+ VIR_FORCE_CLOSE(cmd->handshakeWait[1]);
+ cmd->has_error = errno;
+ return;
+ }
+
+ VIR_DEBUG("Transfer handshake wait=%d notify=%d, "
+ "keep handshake wait=%d notify=%d",
+ cmd->handshakeWait[1], cmd->handshakeNotify[0],
+ cmd->handshakeWait[0], cmd->handshakeNotify[1]);
+ virCommandTransferFD(cmd, cmd->handshakeWait[1]);
+ virCommandTransferFD(cmd, cmd->handshakeNotify[0]);
+ cmd->handshake = true;
+}
+
+/**
+ * virCommandHandshakeWait:
+ * @cmd: command to wait on
+ *
+ * Wait for the child to complete execution of its
+ * hook function. To be called in the parent.
+ */
+int virCommandHandshakeWait(virCommandPtr cmd)
+{
+ char c;
+ int rv;
+ if (!cmd ||cmd->has_error == ENOMEM) {
+ virReportOOMError();
+ return -1;
+ }
+ if (cmd->has_error || !cmd->handshake) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return -1;
+ }
+
+ if (cmd->handshakeWait[0] == -1) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Handshake is already complete"));
+ return -1;
+ }
+
+ VIR_DEBUG("Wait for handshake on %d", cmd->handshakeWait[0]);
+ if ((rv = saferead(cmd->handshakeWait[0], &c, sizeof(c))) != sizeof(c)) {
+ if (rv < 0)
+ virReportSystemError(errno, "%s",
+ _("Unable to wait for child process"));
+ else
+ virReportSystemError(EIO, "%s",
+ _("Child quit during startup handshake"));
+ VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
+ return -1;
+ }
+ if (c != '1') {
+ char *msg;
+ ssize_t len;
+ if (VIR_ALLOC_N(msg, 1024) < 0) {
+ virReportOOMError();
+ VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
+ return -1;
+ }
+ /* Close the handshakeNotify fd before trying to read anything
+ * further on the handshakeWait pipe; so that a child waiting
+ * on our acknowledgment will die rather than deadlock. */
+ VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
+
+ if ((len = saferead(cmd->handshakeWait[0], msg, 1024)) < 0) {
+ VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
+ VIR_FREE(msg);
+ virReportSystemError(errno, "%s",
+ _("No error message from child failure"));
+ return -1;
+ }
+ VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
+ msg[len-1] = '\0';
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+ VIR_FREE(msg);
+ return -1;
+ }
+ VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
+ return 0;
+}
+
+/**
+ * virCommandHandshakeNotify:
+ * @cmd: command to resume
+ *
+ * Notify the child that it is OK to exec() the
+ * real binary now. To be called in the parent.
+ */
+int virCommandHandshakeNotify(virCommandPtr cmd)
+{
+ char c = '1';
+ if (!cmd ||cmd->has_error == ENOMEM) {
+ virReportOOMError();
+ return -1;
+ }
+ if (cmd->has_error || !cmd->handshake) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid use of command API"));
+ return -1;
+ }
+
+ if (cmd->handshakeNotify[1] == -1) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Handshake is already complete"));
+ return -1;
+ }
+
+ VIR_DEBUG("Notify handshake on %d", cmd->handshakeNotify[1]);
+ if (safewrite(cmd->handshakeNotify[1], &c, sizeof(c)) != sizeof(c)) {
+ virReportSystemError(errno, "%s", _("Unable to notify child
process"));
+ VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
+ return -1;
+ }
+ VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
+ return 0;
+}
+
+
+/**
+ * virCommandFree:
+ * @cmd: optional command to free
+ *
+ * Release all resources. The only exception is that if you called
+ * virCommandRunAsync with a non-null pid, then the asynchronous child
+ * is not reaped, and you must call virProcessWait() or virProcessAbort() yourself.
+ */
+void
+virCommandFree(virCommandPtr cmd)
+{
+ int i;
+ if (!cmd)
+ return;
+
+ for (i = 0; i < cmd->transfer_size; i++) {
+ VIR_FORCE_CLOSE(cmd->transfer[i]);
+ }
+
+ VIR_FREE(cmd->inbuf);
+ VIR_FORCE_CLOSE(cmd->outfd);
+ VIR_FORCE_CLOSE(cmd->errfd);
+
+ for (i = 0 ; i < cmd->nargs ; i++)
+ VIR_FREE(cmd->args[i]);
+ VIR_FREE(cmd->args);
+
+ for (i = 0 ; i < cmd->nenv ; i++)
+ VIR_FREE(cmd->env[i]);
+ VIR_FREE(cmd->env);
+
+ VIR_FREE(cmd->pwd);
+
+ if (cmd->handshake) {
+ /* The other 2 fds in these arrays are closed
+ * due to use with virCommandTransferFD
+ */
+ VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
+ VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
+ }
+
+ VIR_FREE(cmd->pidfile);
+
+ if (cmd->reap)
+ virCommandAbort(cmd);
+
+ VIR_FREE(cmd->transfer);
+ VIR_FREE(cmd->preserve);
+
+ VIR_FREE(cmd);
+}
diff --git a/src/util/vircommand.h b/src/util/vircommand.h
new file mode 100644
index 0000000..4c88165
--- /dev/null
+++ b/src/util/vircommand.h
@@ -0,0 +1,166 @@
+/*
+ * vircommand.h: Child command execution
+ *
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VIR_COMMAND_H__
+# define __VIR_COMMAND_H__
+
+# include "internal.h"
+# include "util.h"
+# include "virbuffer.h"
+
+typedef struct _virCommand virCommand;
+typedef virCommand *virCommandPtr;
+
+/* This will execute in the context of the first child
+ * after fork() but before execve(). As such, it is unsafe to
+ * call any function that is not async-signal-safe. */
+typedef int (*virExecHook)(void *data);
+
+int virFork(pid_t *pid) ATTRIBUTE_RETURN_CHECK;
+
+int virRun(const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK;
+
+virCommandPtr virCommandNew(const char *binary) ATTRIBUTE_NONNULL(1);
+
+virCommandPtr virCommandNewArgs(const char *const*args) ATTRIBUTE_NONNULL(1);
+
+virCommandPtr virCommandNewArgList(const char *binary, ...)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL;
+
+virCommandPtr virCommandNewVAList(const char *binary, va_list list)
+ ATTRIBUTE_NONNULL(1);
+
+/* All error report from these setup APIs is
+ * delayed until the Run/RunAsync methods
+ */
+
+void virCommandPreserveFD(virCommandPtr cmd,
+ int fd);
+
+void virCommandTransferFD(virCommandPtr cmd,
+ int fd);
+
+void virCommandSetPidFile(virCommandPtr cmd,
+ const char *pidfile) ATTRIBUTE_NONNULL(2);
+
+void virCommandClearCaps(virCommandPtr cmd);
+
+void virCommandAllowCap(virCommandPtr cmd,
+ int capability);
+
+void virCommandDaemonize(virCommandPtr cmd);
+
+void virCommandNonblockingFDs(virCommandPtr cmd);
+
+void virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
+
+void virCommandAddEnvPair(virCommandPtr cmd,
+ const char *name,
+ const char *value) ATTRIBUTE_NONNULL(2);
+
+void virCommandAddEnvString(virCommandPtr cmd,
+ const char *str) ATTRIBUTE_NONNULL(2);
+
+void virCommandAddEnvBuffer(virCommandPtr cmd,
+ virBufferPtr buf);
+
+void virCommandAddEnvPass(virCommandPtr cmd,
+ const char *name) ATTRIBUTE_NONNULL(2);
+
+void virCommandAddEnvPassCommon(virCommandPtr cmd);
+
+void virCommandAddArg(virCommandPtr cmd,
+ const char *val) ATTRIBUTE_NONNULL(2);
+
+void virCommandAddArgBuffer(virCommandPtr cmd,
+ virBufferPtr buf);
+
+void virCommandAddArgFormat(virCommandPtr cmd,
+ const char *format, ...)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
+
+void virCommandAddArgPair(virCommandPtr cmd,
+ const char *name,
+ const char *val)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+
+void virCommandAddArgSet(virCommandPtr cmd,
+ const char *const*vals) ATTRIBUTE_NONNULL(2);
+
+void virCommandAddArgList(virCommandPtr cmd,
+ ... /* const char *arg, ..., NULL */)
+ ATTRIBUTE_SENTINEL;
+
+void virCommandSetWorkingDirectory(virCommandPtr cmd,
+ const char *pwd) ATTRIBUTE_NONNULL(2);
+
+void virCommandSetInputBuffer(virCommandPtr cmd,
+ const char *inbuf) ATTRIBUTE_NONNULL(2);
+
+void virCommandSetOutputBuffer(virCommandPtr cmd,
+ char **outbuf) ATTRIBUTE_NONNULL(2);
+
+void virCommandSetErrorBuffer(virCommandPtr cmd,
+ char **errbuf) ATTRIBUTE_NONNULL(2);
+
+void virCommandSetInputFD(virCommandPtr cmd,
+ int infd);
+
+void virCommandSetOutputFD(virCommandPtr cmd,
+ int *outfd) ATTRIBUTE_NONNULL(2);
+
+void virCommandSetErrorFD(virCommandPtr cmd,
+ int *errfd) ATTRIBUTE_NONNULL(2);
+
+void virCommandSetPreExecHook(virCommandPtr cmd,
+ virExecHook hook,
+ void *opaque) ATTRIBUTE_NONNULL(2);
+
+void virCommandWriteArgLog(virCommandPtr cmd,
+ int logfd);
+
+char *virCommandToString(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK;
+
+int virCommandExec(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK;
+
+int virCommandRun(virCommandPtr cmd,
+ int *exitstatus) ATTRIBUTE_RETURN_CHECK;
+
+int virCommandRunAsync(virCommandPtr cmd,
+ pid_t *pid) ATTRIBUTE_RETURN_CHECK;
+
+int virCommandWait(virCommandPtr cmd,
+ int *exitstatus) ATTRIBUTE_RETURN_CHECK;
+
+void virCommandRequireHandshake(virCommandPtr cmd);
+
+int virCommandHandshakeWait(virCommandPtr cmd)
+ ATTRIBUTE_RETURN_CHECK;
+
+int virCommandHandshakeNotify(virCommandPtr cmd)
+ ATTRIBUTE_RETURN_CHECK;
+
+void virCommandAbort(virCommandPtr cmd);
+
+void virCommandFree(virCommandPtr cmd);
+
+#endif /* __VIR_COMMAND_H__ */
diff --git a/src/util/virfile.c b/src/util/virfile.c
index c79ef04..d77b726 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -37,7 +37,7 @@
# include <sys/ioctl.h>
#endif
-#include "command.h"
+#include "vircommand.h"
#include "configmake.h"
#include "memory.h"
#include "virterror_internal.h"
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index c345013..e98a2ca 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -26,7 +26,7 @@
#include "virmacaddr.h"
#include "virfile.h"
#include "virterror_internal.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "pci.h"
#include "logging.h"
diff --git a/src/util/virnetdevbandwidth.c b/src/util/virnetdevbandwidth.c
index 19c00b6..bd75a9d 100644
--- a/src/util/virnetdevbandwidth.c
+++ b/src/util/virnetdevbandwidth.c
@@ -23,7 +23,7 @@
#include <config.h>
#include "virnetdevbandwidth.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "virterror_internal.h"
diff --git a/src/util/virnetdevopenvswitch.c b/src/util/virnetdevopenvswitch.c
index 5bce611..983a240 100644
--- a/src/util/virnetdevopenvswitch.c
+++ b/src/util/virnetdevopenvswitch.c
@@ -24,7 +24,7 @@
#include <config.h>
#include "virnetdevopenvswitch.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "virterror_internal.h"
#include "virmacaddr.h"
diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
index 4166ee0..3261337 100644
--- a/src/util/virnetdevveth.c
+++ b/src/util/virnetdevveth.c
@@ -28,7 +28,7 @@
#include "virnetdevveth.h"
#include "memory.h"
#include "logging.h"
-#include "command.h"
+#include "vircommand.h"
#include "virterror_internal.h"
#define VIR_FROM_THIS VIR_FROM_NONE
diff --git a/src/util/virnodesuspend.c b/src/util/virnodesuspend.c
index f80920e..a34ca6a 100644
--- a/src/util/virnodesuspend.c
+++ b/src/util/virnodesuspend.c
@@ -22,7 +22,7 @@
#include <config.h>
#include "virnodesuspend.h"
-#include "command.h"
+#include "vircommand.h"
#include "threads.h"
#include "datatypes.h"
diff --git a/src/vmware/vmware_conf.c b/src/vmware/vmware_conf.c
index 2eed4f8..b32de66 100644
--- a/src/vmware/vmware_conf.c
+++ b/src/vmware/vmware_conf.c
@@ -24,7 +24,7 @@
#include <string.h>
#include <sys/utsname.h>
-#include "command.h"
+#include "vircommand.h"
#include "cpu/cpu.h"
#include "dirname.h"
#include "memory.h"
diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c
index 3e7397f..d9a1333 100644
--- a/src/vmware/vmware_driver.c
+++ b/src/vmware/vmware_driver.c
@@ -30,7 +30,7 @@
#include "memory.h"
#include "util.h"
#include "uuid.h"
-#include "command.h"
+#include "vircommand.h"
#include "vmx.h"
#include "vmware_conf.h"
#include "vmware_driver.h"
diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c
index d2de141..d9174b6 100644
--- a/src/xen/xen_driver.c
+++ b/src/xen/xen_driver.c
@@ -62,7 +62,7 @@
#include "fdstream.h"
#include "virfile.h"
#include "viruri.h"
-#include "command.h"
+#include "vircommand.h"
#include "virnodesuspend.h"
#include "nodeinfo.h"
#include "configmake.h"
diff --git a/tests/commandtest.c b/tests/commandtest.c
index 19bf9ba..f76bc54 100644
--- a/tests/commandtest.c
+++ b/tests/commandtest.c
@@ -33,7 +33,7 @@
#include "nodeinfo.h"
#include "util.h"
#include "memory.h"
-#include "command.h"
+#include "vircommand.h"
#include "virfile.h"
#include "virpidfile.h"
#include "virterror_internal.h"
diff --git a/tests/networkxml2conftest.c b/tests/networkxml2conftest.c
index 1888465..dc0e064 100644
--- a/tests/networkxml2conftest.c
+++ b/tests/networkxml2conftest.c
@@ -11,7 +11,7 @@
#include "internal.h"
#include "testutils.h"
#include "network_conf.h"
-#include "command.h"
+#include "vircommand.h"
#include "memory.h"
#include "network/bridge_driver.h"
diff --git a/tests/reconnect.c b/tests/reconnect.c
index 90af830..4031360 100644
--- a/tests/reconnect.c
+++ b/tests/reconnect.c
@@ -6,7 +6,7 @@
#include "internal.h"
#include "testutils.h"
-#include "command.h"
+#include "vircommand.h"
static void errorHandler(void *userData ATTRIBUTE_UNUSED,
virErrorPtr error ATTRIBUTE_UNUSED) {
diff --git a/tests/statstest.c b/tests/statstest.c
index 8c40082..ad71bf9 100644
--- a/tests/statstest.c
+++ b/tests/statstest.c
@@ -9,7 +9,7 @@
#include "internal.h"
#include "xen/block_stats.h"
#include "testutils.h"
-#include "command.h"
+#include "vircommand.h"
static void testQuietError(void *userData ATTRIBUTE_UNUSED,
virErrorPtr error ATTRIBUTE_UNUSED)
diff --git a/tests/testutils.c b/tests/testutils.c
index e8b48e8..1315cb5 100644
--- a/tests/testutils.c
+++ b/tests/testutils.c
@@ -45,7 +45,7 @@
#include "virterror_internal.h"
#include "virbuffer.h"
#include "logging.h"
-#include "command.h"
+#include "vircommand.h"
#include "virrandom.h"
#include "dirname.h"
#include "virprocess.h"
diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c
index cc260e0..d421feb 100644
--- a/tests/virnettlscontexttest.c
+++ b/tests/virnettlscontexttest.c
@@ -32,7 +32,7 @@
#include "memory.h"
#include "logging.h"
#include "virfile.h"
-#include "command.h"
+#include "vircommand.h"
#include "virsocketaddr.h"
#include "gnutls_1_0_compat.h"
diff --git a/tools/virsh.c b/tools/virsh.c
index 82c03e4..07da077 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -66,7 +66,7 @@
#include "event_poll.h"
#include "configmake.h"
#include "threads.h"
-#include "command.h"
+#include "vircommand.h"
#include "virkeycode.h"
#include "virnetdevbandwidth.h"
#include "virbitmap.h"
--
1.7.11.7