From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list