dcavalca / rpms / qemu

Forked from rpms/qemu 11 months ago
Clone

Blame 0012-scsi-build-qemu-pr-helper.patch

Paolo Bonzini 0fb2b2
From: Paolo Bonzini <pbonzini@redhat.com>
Paolo Bonzini 0fb2b2
Date: Tue, 22 Aug 2017 06:50:18 +0200
59eb7a
Subject: [PATCH] scsi: build qemu-pr-helper
Paolo Bonzini 0fb2b2
Paolo Bonzini 0fb2b2
Introduce a privileged helper to run persistent reservation commands.
Paolo Bonzini 0fb2b2
This lets virtual machines send persistent reservations without using
Paolo Bonzini 0fb2b2
CAP_SYS_RAWIO or out-of-tree patches.  The helper uses Unix permissions
Paolo Bonzini 0fb2b2
and SCM_RIGHTS to restrict access to processes that can access its socket
Paolo Bonzini 0fb2b2
and prove that they have an open file descriptor for a raw SCSI device.
Paolo Bonzini 0fb2b2
Paolo Bonzini 0fb2b2
The next patch will also correct the usage of persistent reservations
Paolo Bonzini 0fb2b2
with multipath devices.
Paolo Bonzini 0fb2b2
Paolo Bonzini 0fb2b2
It would also be possible to support for Linux's IOC_PR_* ioctls in
Paolo Bonzini 0fb2b2
the future, to support NVMe devices.  For now, however, only SCSI is
Paolo Bonzini 0fb2b2
supported.
Paolo Bonzini 0fb2b2
Paolo Bonzini 0fb2b2
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Paolo Bonzini 0fb2b2
---
Paolo Bonzini 0fb2b2
 Makefile                   |   4 +-
Paolo Bonzini 0fb2b2
 configure                  |  14 +-
Paolo Bonzini 0fb2b2
 docs/interop/pr-helper.rst |  83 +++++
Paolo Bonzini 0fb2b2
 docs/pr-manager.rst        |  33 ++
Paolo Bonzini 0fb2b2
 scsi/pr-helper.h           |  41 +++
Paolo Bonzini 0fb2b2
 scsi/qemu-pr-helper.c      | 735 +++++++++++++++++++++++++++++++++++++++++++++
Paolo Bonzini 0fb2b2
 6 files changed, 905 insertions(+), 5 deletions(-)
Paolo Bonzini 0fb2b2
 create mode 100644 docs/interop/pr-helper.rst
Paolo Bonzini 0fb2b2
 create mode 100644 scsi/pr-helper.h
Paolo Bonzini 0fb2b2
 create mode 100644 scsi/qemu-pr-helper.c
Paolo Bonzini 0fb2b2
Paolo Bonzini 0fb2b2
diff --git a/Makefile b/Makefile
59eb7a
index 81447b1f08..f07c0d7e9c 100644
Paolo Bonzini 0fb2b2
--- a/Makefile
Paolo Bonzini 0fb2b2
+++ b/Makefile
59eb7a
@@ -382,6 +382,8 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
Paolo Bonzini 0fb2b2
 fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
Paolo Bonzini 0fb2b2
 fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
Paolo Bonzini 0fb2b2
 
Paolo Bonzini 0fb2b2
+scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
 qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
Paolo Bonzini 0fb2b2
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
Paolo Bonzini 0fb2b2
 
59eb7a
@@ -489,7 +491,7 @@ clean:
Paolo Bonzini 0fb2b2
 	rm -f *.msi
Paolo Bonzini 0fb2b2
 	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
Paolo Bonzini 0fb2b2
 	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
Paolo Bonzini 0fb2b2
-	rm -f fsdev/*.pod
Paolo Bonzini 0fb2b2
+	rm -f fsdev/*.pod scsi/*.pod
Paolo Bonzini 0fb2b2
 	rm -f qemu-img-cmds.h
Paolo Bonzini 0fb2b2
 	rm -f ui/shader/*-vert.h ui/shader/*-frag.h
Paolo Bonzini 0fb2b2
 	@# May not be present in GENERATED_FILES
Paolo Bonzini 0fb2b2
diff --git a/configure b/configure
59eb7a
index dd73cce62f..14bdf9bb31 100755
Paolo Bonzini 0fb2b2
--- a/configure
Paolo Bonzini 0fb2b2
+++ b/configure
59eb7a
@@ -5070,16 +5070,22 @@ if test "$want_tools" = "yes" ; then
Paolo Bonzini 0fb2b2
   fi
Paolo Bonzini 0fb2b2
 fi
Paolo Bonzini 0fb2b2
 if test "$softmmu" = yes ; then
Paolo Bonzini 0fb2b2
-  if test "$virtfs" != no ; then
Paolo Bonzini 0fb2b2
-    if test "$cap" = yes && test "$linux" = yes && test "$attr" = yes ; then
Paolo Bonzini 0fb2b2
+  if test "$linux" = yes; then
Paolo Bonzini 0fb2b2
+    if test "$virtfs" != no && test "$cap" = yes && test "$attr" = yes ; then
Paolo Bonzini 0fb2b2
       virtfs=yes
Paolo Bonzini 0fb2b2
       tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)"
Paolo Bonzini 0fb2b2
     else
Paolo Bonzini 0fb2b2
       if test "$virtfs" = yes; then
Paolo Bonzini 0fb2b2
-        error_exit "VirtFS is supported only on Linux and requires libcap devel and libattr devel"
Paolo Bonzini 0fb2b2
+        error_exit "VirtFS requires libcap devel and libattr devel"
Paolo Bonzini 0fb2b2
       fi
Paolo Bonzini 0fb2b2
       virtfs=no
Paolo Bonzini 0fb2b2
     fi
Paolo Bonzini 0fb2b2
+    tools="$tools scsi/qemu-pr-helper\$(EXESUF)"
Paolo Bonzini 0fb2b2
+  else
Paolo Bonzini 0fb2b2
+    if test "$virtfs" = yes; then
Paolo Bonzini 0fb2b2
+      error_exit "VirtFS is supported only on Linux"
Paolo Bonzini 0fb2b2
+    fi
Paolo Bonzini 0fb2b2
+    virtfs=no
Paolo Bonzini 0fb2b2
   fi
Paolo Bonzini 0fb2b2
 fi
Paolo Bonzini 0fb2b2
 
59eb7a
@@ -6545,7 +6551,7 @@ fi
Paolo Bonzini 0fb2b2
 
Paolo Bonzini 0fb2b2
 # build tree in object directory in case the source is not in the current directory
Paolo Bonzini 0fb2b2
 DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
Paolo Bonzini 0fb2b2
-DIRS="$DIRS docs docs/interop fsdev"
Paolo Bonzini 0fb2b2
+DIRS="$DIRS docs docs/interop fsdev scsi"
Paolo Bonzini 0fb2b2
 DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
Paolo Bonzini 0fb2b2
 DIRS="$DIRS roms/seabios roms/vgabios"
Paolo Bonzini 0fb2b2
 DIRS="$DIRS qapi-generated"
Paolo Bonzini 0fb2b2
diff --git a/docs/interop/pr-helper.rst b/docs/interop/pr-helper.rst
Paolo Bonzini 0fb2b2
new file mode 100644
Paolo Bonzini 0fb2b2
index 0000000000..9f76d5bcf9
Paolo Bonzini 0fb2b2
--- /dev/null
Paolo Bonzini 0fb2b2
+++ b/docs/interop/pr-helper.rst
Paolo Bonzini 0fb2b2
@@ -0,0 +1,83 @@
Paolo Bonzini 0fb2b2
+..
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+======================================
Paolo Bonzini 0fb2b2
+Persistent reservation helper protocol
Paolo Bonzini 0fb2b2
+======================================
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+QEMU's SCSI passthrough devices, ``scsi-block`` and ``scsi-generic``,
Paolo Bonzini 0fb2b2
+can delegate implementation of persistent reservations to an external
Paolo Bonzini 0fb2b2
+(and typically privileged) program.  Persistent Reservations allow
Paolo Bonzini 0fb2b2
+restricting access to block devices to specific initiators in a shared
Paolo Bonzini 0fb2b2
+storage setup.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+For a more detailed reference please refer the the SCSI Primary
Paolo Bonzini 0fb2b2
+Commands standard, specifically the section on Reservations and the
Paolo Bonzini 0fb2b2
+"PERSISTENT RESERVE IN" and "PERSISTENT RESERVE OUT" commands.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+This document describes the socket protocol used between QEMU's
Paolo Bonzini 0fb2b2
+``pr-manager-helper`` object and the external program.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+.. contents::
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+Connection and initialization
Paolo Bonzini 0fb2b2
+-----------------------------
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+All data transmitted on the socket is big-endian.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+After connecting to the helper program's socket, the helper starts a simple
Paolo Bonzini 0fb2b2
+feature negotiation process by writing four bytes corresponding to
Paolo Bonzini 0fb2b2
+the features it exposes (``supported_features``).  QEMU reads it,
Paolo Bonzini 0fb2b2
+then writes four bytes corresponding to the desired features of the
Paolo Bonzini 0fb2b2
+helper program (``requested_features``).
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+If a bit is 1 in ``requested_features`` and 0 in ``supported_features``,
Paolo Bonzini 0fb2b2
+the corresponding feature is not supported by the helper and the connection
Paolo Bonzini 0fb2b2
+is closed.  On the other hand, it is acceptable for a bit to be 0 in
Paolo Bonzini 0fb2b2
+``requested_features`` and 1 in ``supported_features``; in this case,
Paolo Bonzini 0fb2b2
+the helper will not enable the feature.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+Right now no feature is defined, so the two parties always write four
Paolo Bonzini 0fb2b2
+zero bytes.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+Command format
Paolo Bonzini 0fb2b2
+--------------
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+It is invalid to send multiple commands concurrently on the same
Paolo Bonzini 0fb2b2
+socket.  It is however possible to connect multiple sockets to the
Paolo Bonzini 0fb2b2
+helper and send multiple commands to the helper for one or more
Paolo Bonzini 0fb2b2
+file descriptors.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+A command consists of a request and a response.  A request consists
Paolo Bonzini 0fb2b2
+of a 16-byte SCSI CDB.  A file descriptor must be passed to the helper
Paolo Bonzini 0fb2b2
+together with the SCSI CDB using ancillary data.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+The CDB has the following limitations:
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+- the command (stored in the first byte) must be one of 0x5E
Paolo Bonzini 0fb2b2
+  (PERSISTENT RESERVE IN) or 0x5F (PERSISTENT RESERVE OUT).
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+- the allocation length (stored in bytes 7-8 of the CDB for PERSISTENT
Paolo Bonzini 0fb2b2
+  RESERVE IN) or parameter list length (stored in bytes 5-8 of the CDB
Paolo Bonzini 0fb2b2
+  for PERSISTENT RESERVE OUT) is limited to 8 KiB.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+For PERSISTENT RESERVE OUT, the parameter list is sent right after the
Paolo Bonzini 0fb2b2
+CDB.  The length of the parameter list is taken from the CDB itself.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+The helper's reply has the following structure:
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+- 4 bytes for the SCSI status
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+- 4 bytes for the payload size (nonzero only for PERSISTENT RESERVE IN
Paolo Bonzini 0fb2b2
+  and only if the SCSI status is 0x00, i.e. GOOD)
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+- 96 bytes for the SCSI sense data
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+- if the size is nonzero, the payload follows
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+The sense data is always sent to keep the protocol simple, even though
Paolo Bonzini 0fb2b2
+it is only valid if the SCSI status is CHECK CONDITION (0x02).
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+The payload size is always less than or equal to the allocation length
Paolo Bonzini 0fb2b2
+specified in the CDB for the PERSISTENT RESERVE IN command.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+If the protocol is violated, the helper closes the socket.
Paolo Bonzini 0fb2b2
diff --git a/docs/pr-manager.rst b/docs/pr-manager.rst
Paolo Bonzini 0fb2b2
index b6089fb57c..7107e59fb8 100644
Paolo Bonzini 0fb2b2
--- a/docs/pr-manager.rst
Paolo Bonzini 0fb2b2
+++ b/docs/pr-manager.rst
Paolo Bonzini 0fb2b2
@@ -49,3 +49,36 @@ Alternatively, using ``-blockdev``::
Paolo Bonzini 0fb2b2
           -object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock
Paolo Bonzini 0fb2b2
           -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0
Paolo Bonzini 0fb2b2
           -device scsi-block,drive=hd
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+----------------------------------
Paolo Bonzini 0fb2b2
+Invoking :program:`qemu-pr-helper`
Paolo Bonzini 0fb2b2
+----------------------------------
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+QEMU provides an implementation of the persistent reservation helper,
Paolo Bonzini 0fb2b2
+called :program:`qemu-pr-helper`.  The helper should be started as a
Paolo Bonzini 0fb2b2
+system service and supports the following option:
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+-d, --daemon              run in the background
Paolo Bonzini 0fb2b2
+-q, --quiet               decrease verbosity
Paolo Bonzini 0fb2b2
+-f, --pidfile=path        PID file when running as a daemon
Paolo Bonzini 0fb2b2
+-k, --socket=path         path to the socket
Paolo Bonzini 0fb2b2
+-T, --trace=trace-opts    tracing options
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+By default, the socket and PID file are placed in the runtime state
Paolo Bonzini 0fb2b2
+directory, for example :file:`/var/run/qemu-pr-helper.sock` and
Paolo Bonzini 0fb2b2
+:file:`/var/run/qemu-pr-helper.pid`.  The PID file is not created
Paolo Bonzini 0fb2b2
+unless :option:`-d` is passed too.
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+:program:`qemu-pr-helper` can also use the systemd socket activation
Paolo Bonzini 0fb2b2
+protocol.  In this case, the systemd socket unit should specify a
Paolo Bonzini 0fb2b2
+Unix stream socket, like this::
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    [Socket]
Paolo Bonzini 0fb2b2
+    ListenStream=/var/run/qemu-pr-helper.sock
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+After connecting to the socket, :program:`qemu-pr-helper`` can optionally drop
Paolo Bonzini 0fb2b2
+root privileges, except for those capabilities that are needed for
Paolo Bonzini 0fb2b2
+its operation.  To do this, add the following options:
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+-u, --user=user           user to drop privileges to
Paolo Bonzini 0fb2b2
+-g, --group=group         group to drop privileges to
Paolo Bonzini 0fb2b2
diff --git a/scsi/pr-helper.h b/scsi/pr-helper.h
Paolo Bonzini 0fb2b2
new file mode 100644
Paolo Bonzini 0fb2b2
index 0000000000..96c50a9e5f
Paolo Bonzini 0fb2b2
--- /dev/null
Paolo Bonzini 0fb2b2
+++ b/scsi/pr-helper.h
Paolo Bonzini 0fb2b2
@@ -0,0 +1,41 @@
Paolo Bonzini 0fb2b2
+/* Definitions for QEMU's persistent reservation helper daemon
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * Copyright (C) 2017 Red Hat, Inc.
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * Author:
Paolo Bonzini 0fb2b2
+ *   Paolo Bonzini <pbonzini@redhat.com>
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
Paolo Bonzini 0fb2b2
+ * of this software and associated documentation files (the "Software"), to
Paolo Bonzini 0fb2b2
+ * deal in the Software without restriction, including without limitation the
Paolo Bonzini 0fb2b2
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
Paolo Bonzini 0fb2b2
+ * sell copies of the Software, and to permit persons to whom the Software is
Paolo Bonzini 0fb2b2
+ * furnished to do so, subject to the following conditions:
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * The above copyright notice and this permission notice shall be included in
Paolo Bonzini 0fb2b2
+ * all copies or substantial portions of the Software.
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Paolo Bonzini 0fb2b2
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Paolo Bonzini 0fb2b2
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Paolo Bonzini 0fb2b2
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Paolo Bonzini 0fb2b2
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Paolo Bonzini 0fb2b2
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
Paolo Bonzini 0fb2b2
+ * IN THE SOFTWARE.
Paolo Bonzini 0fb2b2
+ */
Paolo Bonzini 0fb2b2
+#ifndef QEMU_PR_HELPER_H
Paolo Bonzini 0fb2b2
+#define QEMU_PR_HELPER_H 1
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#include <stdint.h>
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#define PR_HELPER_CDB_SIZE     16
Paolo Bonzini 0fb2b2
+#define PR_HELPER_SENSE_SIZE   96
Paolo Bonzini 0fb2b2
+#define PR_HELPER_DATA_SIZE    8192
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+typedef struct PRHelperResponse {
Paolo Bonzini 0fb2b2
+    int32_t result;
Paolo Bonzini 0fb2b2
+    int32_t sz;
Paolo Bonzini 0fb2b2
+    uint8_t sense[PR_HELPER_SENSE_SIZE];
Paolo Bonzini 0fb2b2
+} PRHelperResponse;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#endif
Paolo Bonzini 0fb2b2
diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c
Paolo Bonzini 0fb2b2
new file mode 100644
Paolo Bonzini 0fb2b2
index 0000000000..e39efbd529
Paolo Bonzini 0fb2b2
--- /dev/null
Paolo Bonzini 0fb2b2
+++ b/scsi/qemu-pr-helper.c
Paolo Bonzini 0fb2b2
@@ -0,0 +1,735 @@
Paolo Bonzini 0fb2b2
+/*
Paolo Bonzini 0fb2b2
+ * Privileged helper to handle persistent reservation commands for QEMU
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * Copyright (C) 2017 Red Hat, Inc. <pbonzini@redhat.com>
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * This program is free software; you can redistribute it and/or modify
Paolo Bonzini 0fb2b2
+ * it under the terms of the GNU General Public License as published by
Paolo Bonzini 0fb2b2
+ * the Free Software Foundation; under version 2 of the License.
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * This program is distributed in the hope that it will be useful,
Paolo Bonzini 0fb2b2
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
Paolo Bonzini 0fb2b2
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Paolo Bonzini 0fb2b2
+ * GNU General Public License for more details.
Paolo Bonzini 0fb2b2
+ *
Paolo Bonzini 0fb2b2
+ * You should have received a copy of the GNU General Public License
Paolo Bonzini 0fb2b2
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
Paolo Bonzini 0fb2b2
+ */
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#include "qemu/osdep.h"
Paolo Bonzini 0fb2b2
+#include <getopt.h>
Paolo Bonzini 0fb2b2
+#include <sys/ioctl.h>
Paolo Bonzini 0fb2b2
+#include <linux/dm-ioctl.h>
Paolo Bonzini 0fb2b2
+#include <scsi/sg.h>
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#ifdef CONFIG_LIBCAP
Paolo Bonzini 0fb2b2
+#include <cap-ng.h>
Paolo Bonzini 0fb2b2
+#endif
Paolo Bonzini 0fb2b2
+#include <pwd.h>
Paolo Bonzini 0fb2b2
+#include <grp.h>
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#include "qapi/error.h"
Paolo Bonzini 0fb2b2
+#include "qemu-common.h"
Paolo Bonzini 0fb2b2
+#include "qemu/cutils.h"
Paolo Bonzini 0fb2b2
+#include "qemu/main-loop.h"
Paolo Bonzini 0fb2b2
+#include "qemu/error-report.h"
Paolo Bonzini 0fb2b2
+#include "qemu/config-file.h"
Paolo Bonzini 0fb2b2
+#include "qemu/bswap.h"
Paolo Bonzini 0fb2b2
+#include "qemu/log.h"
Paolo Bonzini 0fb2b2
+#include "qemu/systemd.h"
Paolo Bonzini 0fb2b2
+#include "qapi/util.h"
Paolo Bonzini 0fb2b2
+#include "qapi/qmp/qstring.h"
Paolo Bonzini 0fb2b2
+#include "io/channel-socket.h"
Paolo Bonzini 0fb2b2
+#include "trace/control.h"
Paolo Bonzini 0fb2b2
+#include "qemu-version.h"
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#include "block/aio.h"
Paolo Bonzini 0fb2b2
+#include "block/thread-pool.h"
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#include "scsi/constants.h"
Paolo Bonzini 0fb2b2
+#include "scsi/utils.h"
Paolo Bonzini 0fb2b2
+#include "pr-helper.h"
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#define PR_OUT_FIXED_PARAM_SIZE 24
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static char *socket_path;
Paolo Bonzini 0fb2b2
+static char *pidfile;
Paolo Bonzini 0fb2b2
+static enum { RUNNING, TERMINATE, TERMINATING } state;
Paolo Bonzini 0fb2b2
+static QIOChannelSocket *server_ioc;
Paolo Bonzini 0fb2b2
+static int server_watch;
Paolo Bonzini 0fb2b2
+static int num_active_sockets = 1;
Paolo Bonzini 0fb2b2
+static int verbose;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#ifdef CONFIG_LIBCAP
Paolo Bonzini 0fb2b2
+static int uid = -1;
Paolo Bonzini 0fb2b2
+static int gid = -1;
Paolo Bonzini 0fb2b2
+#endif
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static void usage(const char *name)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    (printf) (
Paolo Bonzini 0fb2b2
+"Usage: %s [OPTIONS] FILE\n"
Paolo Bonzini 0fb2b2
+"Persistent Reservation helper program for QEMU\n"
Paolo Bonzini 0fb2b2
+"\n"
Paolo Bonzini 0fb2b2
+"  -h, --help                display this help and exit\n"
Paolo Bonzini 0fb2b2
+"  -V, --version             output version information and exit\n"
Paolo Bonzini 0fb2b2
+"\n"
Paolo Bonzini 0fb2b2
+"  -d, --daemon              run in the background\n"
Paolo Bonzini 0fb2b2
+"  -f, --pidfile=PATH        PID file when running as a daemon\n"
Paolo Bonzini 0fb2b2
+"                            (default '%s')\n"
Paolo Bonzini 0fb2b2
+"  -k, --socket=PATH         path to the unix socket\n"
Paolo Bonzini 0fb2b2
+"                            (default '%s')\n"
Paolo Bonzini 0fb2b2
+"  -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
Paolo Bonzini 0fb2b2
+"                            specify tracing options\n"
Paolo Bonzini 0fb2b2
+#ifdef CONFIG_LIBCAP
Paolo Bonzini 0fb2b2
+"  -u, --user=USER           user to drop privileges to\n"
Paolo Bonzini 0fb2b2
+"  -g, --group=GROUP         group to drop privileges to\n"
Paolo Bonzini 0fb2b2
+#endif
Paolo Bonzini 0fb2b2
+"\n"
Paolo Bonzini 0fb2b2
+QEMU_HELP_BOTTOM "\n"
Paolo Bonzini 0fb2b2
+    , name, pidfile, socket_path);
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static void version(const char *name)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    printf(
Paolo Bonzini 0fb2b2
+"%s " QEMU_VERSION QEMU_PKGVERSION "\n"
Paolo Bonzini 0fb2b2
+"Written by Paolo Bonzini.\n"
Paolo Bonzini 0fb2b2
+"\n"
Paolo Bonzini 0fb2b2
+QEMU_COPYRIGHT "\n"
Paolo Bonzini 0fb2b2
+"This is free software; see the source for copying conditions.  There is NO\n"
Paolo Bonzini 0fb2b2
+"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
Paolo Bonzini 0fb2b2
+    , name);
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static void write_pidfile(void)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    int pidfd;
Paolo Bonzini 0fb2b2
+    char pidstr[32];
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
Paolo Bonzini 0fb2b2
+    if (pidfd == -1) {
Paolo Bonzini 0fb2b2
+        error_report("Cannot open pid file, %s", strerror(errno));
Paolo Bonzini 0fb2b2
+        exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (lockf(pidfd, F_TLOCK, 0)) {
Paolo Bonzini 0fb2b2
+        error_report("Cannot lock pid file, %s", strerror(errno));
Paolo Bonzini 0fb2b2
+        goto fail;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+    if (ftruncate(pidfd, 0)) {
Paolo Bonzini 0fb2b2
+        error_report("Failed to truncate pid file");
Paolo Bonzini 0fb2b2
+        goto fail;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
Paolo Bonzini 0fb2b2
+    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
Paolo Bonzini 0fb2b2
+        error_report("Failed to write pid file");
Paolo Bonzini 0fb2b2
+        goto fail;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+    return;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+fail:
Paolo Bonzini 0fb2b2
+    unlink(pidfile);
Paolo Bonzini 0fb2b2
+    close(pidfd);
Paolo Bonzini 0fb2b2
+    exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+/* SG_IO support */
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+typedef struct PRHelperSGIOData {
Paolo Bonzini 0fb2b2
+    int fd;
Paolo Bonzini 0fb2b2
+    const uint8_t *cdb;
Paolo Bonzini 0fb2b2
+    uint8_t *sense;
Paolo Bonzini 0fb2b2
+    uint8_t *buf;
Paolo Bonzini 0fb2b2
+    int sz;              /* input/output */
Paolo Bonzini 0fb2b2
+    int dir;
Paolo Bonzini 0fb2b2
+} PRHelperSGIOData;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static int do_sgio_worker(void *opaque)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    PRHelperSGIOData *data = opaque;
Paolo Bonzini 0fb2b2
+    struct sg_io_hdr io_hdr;
Paolo Bonzini 0fb2b2
+    int ret;
Paolo Bonzini 0fb2b2
+    int status;
Paolo Bonzini 0fb2b2
+    SCSISense sense_code;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    memset(data->sense, 0, PR_HELPER_SENSE_SIZE);
Paolo Bonzini 0fb2b2
+    memset(&io_hdr, 0, sizeof(io_hdr));
Paolo Bonzini 0fb2b2
+    io_hdr.interface_id = 'S';
Paolo Bonzini 0fb2b2
+    io_hdr.cmd_len = PR_HELPER_CDB_SIZE;
Paolo Bonzini 0fb2b2
+    io_hdr.cmdp = (uint8_t *)data->cdb;
Paolo Bonzini 0fb2b2
+    io_hdr.sbp = data->sense;
Paolo Bonzini 0fb2b2
+    io_hdr.mx_sb_len = PR_HELPER_SENSE_SIZE;
Paolo Bonzini 0fb2b2
+    io_hdr.timeout = 1;
Paolo Bonzini 0fb2b2
+    io_hdr.dxfer_direction = data->dir;
Paolo Bonzini 0fb2b2
+    io_hdr.dxferp = (char *)data->buf;
Paolo Bonzini 0fb2b2
+    io_hdr.dxfer_len = data->sz;
Paolo Bonzini 0fb2b2
+    ret = ioctl(data->fd, SG_IO, &io_hdr);
Paolo Bonzini 0fb2b2
+    status = sg_io_sense_from_errno(ret < 0 ? errno : 0, &io_hdr,
Paolo Bonzini 0fb2b2
+                                    &sense_code);
Paolo Bonzini 0fb2b2
+    if (status == GOOD) {
Paolo Bonzini 0fb2b2
+        data->sz -= io_hdr.resid;
Paolo Bonzini 0fb2b2
+    } else {
Paolo Bonzini 0fb2b2
+        data->sz = 0;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (status == CHECK_CONDITION &&
Paolo Bonzini 0fb2b2
+        !(io_hdr.driver_status & SG_ERR_DRIVER_SENSE)) {
Paolo Bonzini 0fb2b2
+        scsi_build_sense(data->sense, sense_code);
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    return status;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static int do_sgio(int fd, const uint8_t *cdb, uint8_t *sense,
Paolo Bonzini 0fb2b2
+                    uint8_t *buf, int *sz, int dir)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context());
Paolo Bonzini 0fb2b2
+    int r;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    PRHelperSGIOData data = {
Paolo Bonzini 0fb2b2
+        .fd = fd,
Paolo Bonzini 0fb2b2
+        .cdb = cdb,
Paolo Bonzini 0fb2b2
+        .sense = sense,
Paolo Bonzini 0fb2b2
+        .buf = buf,
Paolo Bonzini 0fb2b2
+        .sz = *sz,
Paolo Bonzini 0fb2b2
+        .dir = dir,
Paolo Bonzini 0fb2b2
+    };
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    r = thread_pool_submit_co(pool, do_sgio_worker, &data);
Paolo Bonzini 0fb2b2
+    *sz = data.sz;
Paolo Bonzini 0fb2b2
+    return r;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense,
Paolo Bonzini 0fb2b2
+                    uint8_t *data, int *resp_sz)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    return do_sgio(fd, cdb, sense, data, resp_sz,
Paolo Bonzini 0fb2b2
+                   SG_DXFER_FROM_DEV);
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense,
Paolo Bonzini 0fb2b2
+                     const uint8_t *param, int sz)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    int resp_sz = sz;
Paolo Bonzini 0fb2b2
+    return do_sgio(fd, cdb, sense, (uint8_t *)param, &resp_sz,
Paolo Bonzini 0fb2b2
+                   SG_DXFER_TO_DEV);
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+/* Client */
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+typedef struct PRHelperClient {
Paolo Bonzini 0fb2b2
+    QIOChannelSocket *ioc;
Paolo Bonzini 0fb2b2
+    Coroutine *co;
Paolo Bonzini 0fb2b2
+    int fd;
Paolo Bonzini 0fb2b2
+    uint8_t data[PR_HELPER_DATA_SIZE];
Paolo Bonzini 0fb2b2
+} PRHelperClient;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+typedef struct PRHelperRequest {
Paolo Bonzini 0fb2b2
+    int fd;
Paolo Bonzini 0fb2b2
+    size_t sz;
Paolo Bonzini 0fb2b2
+    uint8_t cdb[PR_HELPER_CDB_SIZE];
Paolo Bonzini 0fb2b2
+} PRHelperRequest;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static int coroutine_fn prh_read(PRHelperClient *client, void *buf, int sz,
Paolo Bonzini 0fb2b2
+                                 Error **errp)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    int ret = 0;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    while (sz > 0) {
Paolo Bonzini 0fb2b2
+        int *fds = NULL;
Paolo Bonzini 0fb2b2
+        size_t nfds = 0;
Paolo Bonzini 0fb2b2
+        int i;
Paolo Bonzini 0fb2b2
+        struct iovec iov;
Paolo Bonzini 0fb2b2
+        ssize_t n_read;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        iov.iov_base = buf;
Paolo Bonzini 0fb2b2
+        iov.iov_len = sz;
Paolo Bonzini 0fb2b2
+        n_read = qio_channel_readv_full(QIO_CHANNEL(client->ioc), &iov, 1,
Paolo Bonzini 0fb2b2
+                                        &fds, &nfds, errp);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        if (n_read == QIO_CHANNEL_ERR_BLOCK) {
Paolo Bonzini 0fb2b2
+            qio_channel_yield(QIO_CHANNEL(client->ioc), G_IO_IN);
Paolo Bonzini 0fb2b2
+            continue;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        if (n_read <= 0) {
Paolo Bonzini 0fb2b2
+            ret = n_read ? n_read : -1;
Paolo Bonzini 0fb2b2
+            goto err;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        /* Stash one file descriptor per request.  */
Paolo Bonzini 0fb2b2
+        if (nfds) {
Paolo Bonzini 0fb2b2
+            bool too_many = false;
Paolo Bonzini 0fb2b2
+            for (i = 0; i < nfds; i++) {
Paolo Bonzini 0fb2b2
+                if (client->fd == -1) {
Paolo Bonzini 0fb2b2
+                    client->fd = fds[i];
Paolo Bonzini 0fb2b2
+                } else {
Paolo Bonzini 0fb2b2
+                    close(fds[i]);
Paolo Bonzini 0fb2b2
+                    too_many = true;
Paolo Bonzini 0fb2b2
+                }
Paolo Bonzini 0fb2b2
+            }
Paolo Bonzini 0fb2b2
+            g_free(fds);
Paolo Bonzini 0fb2b2
+            if (too_many) {
Paolo Bonzini 0fb2b2
+                ret = -1;
Paolo Bonzini 0fb2b2
+                goto err;
Paolo Bonzini 0fb2b2
+            }
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        buf += n_read;
Paolo Bonzini 0fb2b2
+        sz -= n_read;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    return 0;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+err:
Paolo Bonzini 0fb2b2
+    if (client->fd != -1) {
Paolo Bonzini 0fb2b2
+        close(client->fd);
Paolo Bonzini 0fb2b2
+        client->fd = -1;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+    return ret;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static int coroutine_fn prh_read_request(PRHelperClient *client,
Paolo Bonzini 0fb2b2
+                                         PRHelperRequest *req,
Paolo Bonzini 0fb2b2
+                                         PRHelperResponse *resp, Error **errp)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    uint32_t sz;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (prh_read(client, req->cdb, sizeof(req->cdb), NULL) < 0) {
Paolo Bonzini 0fb2b2
+        return -1;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (client->fd == -1) {
Paolo Bonzini 0fb2b2
+        error_setg(errp, "No file descriptor in request.");
Paolo Bonzini 0fb2b2
+        return -1;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (req->cdb[0] != PERSISTENT_RESERVE_OUT &&
Paolo Bonzini 0fb2b2
+        req->cdb[0] != PERSISTENT_RESERVE_IN) {
Paolo Bonzini 0fb2b2
+        error_setg(errp, "Invalid CDB, closing socket.");
Paolo Bonzini 0fb2b2
+        goto out_close;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    sz = scsi_cdb_xfer(req->cdb);
Paolo Bonzini 0fb2b2
+    if (sz > sizeof(client->data)) {
Paolo Bonzini 0fb2b2
+        goto out_close;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (req->cdb[0] == PERSISTENT_RESERVE_OUT) {
Paolo Bonzini 0fb2b2
+        if (qio_channel_read_all(QIO_CHANNEL(client->ioc),
Paolo Bonzini 0fb2b2
+                                 (char *)client->data, sz,
Paolo Bonzini 0fb2b2
+                                 errp) < 0) {
Paolo Bonzini 0fb2b2
+            goto out_close;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        if ((fcntl(client->fd, F_GETFL) & O_ACCMODE) == O_RDONLY) {
Paolo Bonzini 0fb2b2
+            scsi_build_sense(resp->sense, SENSE_CODE(INVALID_OPCODE));
Paolo Bonzini 0fb2b2
+            sz = 0;
Paolo Bonzini 0fb2b2
+        } else if (sz < PR_OUT_FIXED_PARAM_SIZE) {
Paolo Bonzini 0fb2b2
+            /* Illegal request, Parameter list length error.  This isn't fatal;
Paolo Bonzini 0fb2b2
+             * we have read the data, send an error without closing the socket.
Paolo Bonzini 0fb2b2
+             */
Paolo Bonzini 0fb2b2
+            scsi_build_sense(resp->sense, SENSE_CODE(INVALID_PARAM_LEN));
Paolo Bonzini 0fb2b2
+            sz = 0;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        if (sz == 0) {
Paolo Bonzini 0fb2b2
+            resp->result = CHECK_CONDITION;
Paolo Bonzini 0fb2b2
+            close(client->fd);
Paolo Bonzini 0fb2b2
+            client->fd = -1;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    req->fd = client->fd;
Paolo Bonzini 0fb2b2
+    req->sz = sz;
Paolo Bonzini 0fb2b2
+    client->fd = -1;
Paolo Bonzini 0fb2b2
+    return sz;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+out_close:
Paolo Bonzini 0fb2b2
+    close(client->fd);
Paolo Bonzini 0fb2b2
+    client->fd = -1;
Paolo Bonzini 0fb2b2
+    return -1;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static int coroutine_fn prh_write_response(PRHelperClient *client,
Paolo Bonzini 0fb2b2
+                                           PRHelperRequest *req,
Paolo Bonzini 0fb2b2
+                                           PRHelperResponse *resp, Error **errp)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    ssize_t r;
Paolo Bonzini 0fb2b2
+    size_t sz;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (req->cdb[0] == PERSISTENT_RESERVE_IN && resp->result == GOOD) {
Paolo Bonzini 0fb2b2
+        assert(resp->sz <= req->sz && resp->sz <= sizeof(client->data));
Paolo Bonzini 0fb2b2
+    } else {
Paolo Bonzini 0fb2b2
+        assert(resp->sz == 0);
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    sz = resp->sz;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    resp->result = cpu_to_be32(resp->result);
Paolo Bonzini 0fb2b2
+    resp->sz = cpu_to_be32(resp->sz);
Paolo Bonzini 0fb2b2
+    r = qio_channel_write_all(QIO_CHANNEL(client->ioc),
Paolo Bonzini 0fb2b2
+                              (char *) resp, sizeof(*resp), errp);
Paolo Bonzini 0fb2b2
+    if (r < 0) {
Paolo Bonzini 0fb2b2
+        return r;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    r = qio_channel_write_all(QIO_CHANNEL(client->ioc),
Paolo Bonzini 0fb2b2
+                              (char *) client->data,
Paolo Bonzini 0fb2b2
+                              sz, errp);
Paolo Bonzini 0fb2b2
+    return r < 0 ? r : 0;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static void coroutine_fn prh_co_entry(void *opaque)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    PRHelperClient *client = opaque;
Paolo Bonzini 0fb2b2
+    Error *local_err = NULL;
Paolo Bonzini 0fb2b2
+    uint32_t flags;
Paolo Bonzini 0fb2b2
+    int r;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    qio_channel_set_blocking(QIO_CHANNEL(client->ioc),
Paolo Bonzini 0fb2b2
+                             false, NULL);
Paolo Bonzini 0fb2b2
+    qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc),
Paolo Bonzini 0fb2b2
+                                   qemu_get_aio_context());
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    /* A very simple negotiation for future extensibility.  No features
Paolo Bonzini 0fb2b2
+     * are defined so write 0.
Paolo Bonzini 0fb2b2
+     */
Paolo Bonzini 0fb2b2
+    flags = cpu_to_be32(0);
Paolo Bonzini 0fb2b2
+    r = qio_channel_write_all(QIO_CHANNEL(client->ioc),
Paolo Bonzini 0fb2b2
+                             (char *) &flags, sizeof(flags), NULL);
Paolo Bonzini 0fb2b2
+    if (r < 0) {
Paolo Bonzini 0fb2b2
+        goto out;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    r = qio_channel_read_all(QIO_CHANNEL(client->ioc),
Paolo Bonzini 0fb2b2
+                             (char *) &flags, sizeof(flags), NULL);
Paolo Bonzini 0fb2b2
+    if (be32_to_cpu(flags) != 0 || r < 0) {
Paolo Bonzini 0fb2b2
+        goto out;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    while (atomic_read(&state) == RUNNING) {
Paolo Bonzini 0fb2b2
+        PRHelperRequest req;
Paolo Bonzini 0fb2b2
+        PRHelperResponse resp;
Paolo Bonzini 0fb2b2
+        int sz;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        sz = prh_read_request(client, &req, &resp, &local_err);
Paolo Bonzini 0fb2b2
+        if (sz < 0) {
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        if (sz > 0) {
Paolo Bonzini 0fb2b2
+            num_active_sockets++;
Paolo Bonzini 0fb2b2
+            if (req.cdb[0] == PERSISTENT_RESERVE_OUT) {
Paolo Bonzini 0fb2b2
+                r = do_pr_out(req.fd, req.cdb, resp.sense,
Paolo Bonzini 0fb2b2
+                              client->data, sz);
Paolo Bonzini 0fb2b2
+                resp.sz = 0;
Paolo Bonzini 0fb2b2
+            } else {
Paolo Bonzini 0fb2b2
+                resp.sz = sizeof(client->data);
Paolo Bonzini 0fb2b2
+                r = do_pr_in(req.fd, req.cdb, resp.sense,
Paolo Bonzini 0fb2b2
+                             client->data, &resp.sz);
Paolo Bonzini 0fb2b2
+                resp.sz = MIN(resp.sz, sz);
Paolo Bonzini 0fb2b2
+            }
Paolo Bonzini 0fb2b2
+            num_active_sockets--;
Paolo Bonzini 0fb2b2
+            close(req.fd);
Paolo Bonzini 0fb2b2
+            if (r == -1) {
Paolo Bonzini 0fb2b2
+                break;
Paolo Bonzini 0fb2b2
+            }
Paolo Bonzini 0fb2b2
+            resp.result = r;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        if (prh_write_response(client, &req, &resp, &local_err) < 0) {
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (local_err) {
Paolo Bonzini 0fb2b2
+        if (verbose == 0) {
Paolo Bonzini 0fb2b2
+            error_free(local_err);
Paolo Bonzini 0fb2b2
+        } else {
Paolo Bonzini 0fb2b2
+            error_report_err(local_err);
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+out:
Paolo Bonzini 0fb2b2
+    qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc));
Paolo Bonzini 0fb2b2
+    object_unref(OBJECT(client->ioc));
Paolo Bonzini 0fb2b2
+    g_free(client);
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static gboolean accept_client(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    QIOChannelSocket *cioc;
Paolo Bonzini 0fb2b2
+    PRHelperClient *prh;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
Paolo Bonzini 0fb2b2
+                                     NULL);
Paolo Bonzini 0fb2b2
+    if (!cioc) {
Paolo Bonzini 0fb2b2
+        return TRUE;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    prh = g_new(PRHelperClient, 1);
Paolo Bonzini 0fb2b2
+    prh->ioc = cioc;
Paolo Bonzini 0fb2b2
+    prh->fd = -1;
Paolo Bonzini 0fb2b2
+    prh->co = qemu_coroutine_create(prh_co_entry, prh);
Paolo Bonzini 0fb2b2
+    qemu_coroutine_enter(prh->co);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    return TRUE;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+/*
Paolo Bonzini 0fb2b2
+ * Check socket parameters compatibility when socket activation is used.
Paolo Bonzini 0fb2b2
+ */
Paolo Bonzini 0fb2b2
+static const char *socket_activation_validate_opts(void)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    if (socket_path != NULL) {
Paolo Bonzini 0fb2b2
+        return "Unix socket can't be set when using socket activation";
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    return NULL;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static void compute_default_paths(void)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    if (!socket_path) {
Paolo Bonzini 0fb2b2
+        socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock");
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static void termsig_handler(int signum)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    atomic_cmpxchg(&state, RUNNING, TERMINATE);
Paolo Bonzini 0fb2b2
+    qemu_notify_event();
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+static void close_server_socket(void)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    assert(server_ioc);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    g_source_remove(server_watch);
Paolo Bonzini 0fb2b2
+    server_watch = -1;
Paolo Bonzini 0fb2b2
+    object_unref(OBJECT(server_ioc));
Paolo Bonzini 0fb2b2
+    num_active_sockets--;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#ifdef CONFIG_LIBCAP
Paolo Bonzini 0fb2b2
+static int drop_privileges(void)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    /* clear all capabilities */
Paolo Bonzini 0fb2b2
+    capng_clear(CAPNG_SELECT_BOTH);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
Paolo Bonzini 0fb2b2
+                     CAP_SYS_RAWIO) < 0) {
Paolo Bonzini 0fb2b2
+        return -1;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    /* Change user/group id, retaining the capabilities.  Because file descriptors
Paolo Bonzini 0fb2b2
+     * are passed via SCM_RIGHTS, we don't need supplementary groups (and in
Paolo Bonzini 0fb2b2
+     * fact the helper can run as "nobody").
Paolo Bonzini 0fb2b2
+     */
Paolo Bonzini 0fb2b2
+    if (capng_change_id(uid != -1 ? uid : getuid(),
Paolo Bonzini 0fb2b2
+                        gid != -1 ? gid : getgid(),
Paolo Bonzini 0fb2b2
+                        CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING)) {
Paolo Bonzini 0fb2b2
+        return -1;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    return 0;
Paolo Bonzini 0fb2b2
+}
Paolo Bonzini 0fb2b2
+#endif
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+int main(int argc, char **argv)
Paolo Bonzini 0fb2b2
+{
Paolo Bonzini 0fb2b2
+    const char *sopt = "hVk:fdT:u:g:q";
Paolo Bonzini 0fb2b2
+    struct option lopt[] = {
Paolo Bonzini 0fb2b2
+        { "help", no_argument, NULL, 'h' },
Paolo Bonzini 0fb2b2
+        { "version", no_argument, NULL, 'V' },
Paolo Bonzini 0fb2b2
+        { "socket", required_argument, NULL, 'k' },
Paolo Bonzini 0fb2b2
+        { "pidfile", no_argument, NULL, 'f' },
Paolo Bonzini 0fb2b2
+        { "daemon", no_argument, NULL, 'd' },
Paolo Bonzini 0fb2b2
+        { "trace", required_argument, NULL, 'T' },
Paolo Bonzini 0fb2b2
+        { "user", required_argument, NULL, 'u' },
Paolo Bonzini 0fb2b2
+        { "group", required_argument, NULL, 'g' },
Paolo Bonzini 0fb2b2
+        { "quiet", no_argument, NULL, 'q' },
Paolo Bonzini 0fb2b2
+        { NULL, 0, NULL, 0 }
Paolo Bonzini 0fb2b2
+    };
Paolo Bonzini 0fb2b2
+    int opt_ind = 0;
Paolo Bonzini 0fb2b2
+    int quiet = 0;
Paolo Bonzini 0fb2b2
+    char ch;
Paolo Bonzini 0fb2b2
+    Error *local_err = NULL;
Paolo Bonzini 0fb2b2
+    char *trace_file = NULL;
Paolo Bonzini 0fb2b2
+    bool daemonize = false;
Paolo Bonzini 0fb2b2
+    unsigned socket_activation;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    struct sigaction sa_sigterm;
Paolo Bonzini 0fb2b2
+    memset(&sa_sigterm, 0, sizeof(sa_sigterm));
Paolo Bonzini 0fb2b2
+    sa_sigterm.sa_handler = termsig_handler;
Paolo Bonzini 0fb2b2
+    sigaction(SIGTERM, &sa_sigterm, NULL);
Paolo Bonzini 0fb2b2
+    sigaction(SIGINT, &sa_sigterm, NULL);
Paolo Bonzini 0fb2b2
+    sigaction(SIGHUP, &sa_sigterm, NULL);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    signal(SIGPIPE, SIG_IGN);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    module_call_init(MODULE_INIT_TRACE);
Paolo Bonzini 0fb2b2
+    module_call_init(MODULE_INIT_QOM);
Paolo Bonzini 0fb2b2
+    qemu_add_opts(&qemu_trace_opts);
Paolo Bonzini 0fb2b2
+    qemu_init_exec_dir(argv[0]);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid");
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
Paolo Bonzini 0fb2b2
+        switch (ch) {
Paolo Bonzini 0fb2b2
+        case 'k':
Paolo Bonzini 0fb2b2
+            socket_path = optarg;
Paolo Bonzini 0fb2b2
+            if (socket_path[0] != '/') {
Paolo Bonzini 0fb2b2
+                error_report("socket path must be absolute");
Paolo Bonzini 0fb2b2
+                exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+            }
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        case 'f':
Paolo Bonzini 0fb2b2
+            pidfile = optarg;
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+#ifdef CONFIG_LIBCAP
Paolo Bonzini 0fb2b2
+        case 'u': {
Paolo Bonzini 0fb2b2
+            unsigned long res;
Paolo Bonzini 0fb2b2
+            struct passwd *userinfo = getpwnam(optarg);
Paolo Bonzini 0fb2b2
+            if (userinfo) {
Paolo Bonzini 0fb2b2
+                uid = userinfo->pw_uid;
Paolo Bonzini 0fb2b2
+            } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 &&
Paolo Bonzini 0fb2b2
+                       (uid_t)res == res) {
Paolo Bonzini 0fb2b2
+                uid = res;
Paolo Bonzini 0fb2b2
+            } else {
Paolo Bonzini 0fb2b2
+                error_report("invalid user '%s'", optarg);
Paolo Bonzini 0fb2b2
+                exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+            }
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        case 'g': {
Paolo Bonzini 0fb2b2
+            unsigned long res;
Paolo Bonzini 0fb2b2
+            struct group *groupinfo = getgrnam(optarg);
Paolo Bonzini 0fb2b2
+            if (groupinfo) {
Paolo Bonzini 0fb2b2
+                gid = groupinfo->gr_gid;
Paolo Bonzini 0fb2b2
+            } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 &&
Paolo Bonzini 0fb2b2
+                       (gid_t)res == res) {
Paolo Bonzini 0fb2b2
+                gid = res;
Paolo Bonzini 0fb2b2
+            } else {
Paolo Bonzini 0fb2b2
+                error_report("invalid group '%s'", optarg);
Paolo Bonzini 0fb2b2
+                exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+            }
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+#else
Paolo Bonzini 0fb2b2
+        case 'u':
Paolo Bonzini 0fb2b2
+        case 'g':
Paolo Bonzini 0fb2b2
+            error_report("-%c not supported by this %s", ch, argv[0]);
Paolo Bonzini 0fb2b2
+            exit(1);
Paolo Bonzini 0fb2b2
+#endif
Paolo Bonzini 0fb2b2
+        case 'd':
Paolo Bonzini 0fb2b2
+            daemonize = true;
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        case 'q':
Paolo Bonzini 0fb2b2
+            quiet = 1;
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        case 'T':
Paolo Bonzini 0fb2b2
+            g_free(trace_file);
Paolo Bonzini 0fb2b2
+            trace_file = trace_opt_parse(optarg);
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        case 'V':
Paolo Bonzini 0fb2b2
+            version(argv[0]);
Paolo Bonzini 0fb2b2
+            exit(EXIT_SUCCESS);
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        case 'h':
Paolo Bonzini 0fb2b2
+            usage(argv[0]);
Paolo Bonzini 0fb2b2
+            exit(EXIT_SUCCESS);
Paolo Bonzini 0fb2b2
+            break;
Paolo Bonzini 0fb2b2
+        case '?':
Paolo Bonzini 0fb2b2
+            error_report("Try `%s --help' for more information.", argv[0]);
Paolo Bonzini 0fb2b2
+            exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    /* set verbosity */
Paolo Bonzini 0fb2b2
+    verbose = !quiet;
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (!trace_init_backends()) {
Paolo Bonzini 0fb2b2
+        exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+    trace_init_file(trace_file);
Paolo Bonzini 0fb2b2
+    qemu_set_log(LOG_TRACE);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    socket_activation = check_socket_activation();
Paolo Bonzini 0fb2b2
+    if (socket_activation == 0) {
Paolo Bonzini 0fb2b2
+        SocketAddress saddr;
Paolo Bonzini 0fb2b2
+        compute_default_paths();
Paolo Bonzini 0fb2b2
+        saddr = (SocketAddress){
Paolo Bonzini 0fb2b2
+            .type = SOCKET_ADDRESS_TYPE_UNIX,
Paolo Bonzini 0fb2b2
+            .u.q_unix.path = g_strdup(socket_path)
Paolo Bonzini 0fb2b2
+        };
Paolo Bonzini 0fb2b2
+        server_ioc = qio_channel_socket_new();
Paolo Bonzini 0fb2b2
+        if (qio_channel_socket_listen_sync(server_ioc, &saddr, &local_err) < 0) {
Paolo Bonzini 0fb2b2
+            object_unref(OBJECT(server_ioc));
Paolo Bonzini 0fb2b2
+            error_report_err(local_err);
Paolo Bonzini 0fb2b2
+            return 1;
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        g_free(saddr.u.q_unix.path);
Paolo Bonzini 0fb2b2
+    } else {
Paolo Bonzini 0fb2b2
+        /* Using socket activation - check user didn't use -p etc. */
Paolo Bonzini 0fb2b2
+        const char *err_msg = socket_activation_validate_opts();
Paolo Bonzini 0fb2b2
+        if (err_msg != NULL) {
Paolo Bonzini 0fb2b2
+            error_report("%s", err_msg);
Paolo Bonzini 0fb2b2
+            exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+        /* Can only listen on a single socket.  */
Paolo Bonzini 0fb2b2
+        if (socket_activation > 1) {
Paolo Bonzini 0fb2b2
+            error_report("%s does not support socket activation with LISTEN_FDS > 1",
Paolo Bonzini 0fb2b2
+                         argv[0]);
Paolo Bonzini 0fb2b2
+            exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        server_ioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD,
Paolo Bonzini 0fb2b2
+                                               &local_err);
Paolo Bonzini 0fb2b2
+        if (server_ioc == NULL) {
Paolo Bonzini 0fb2b2
+            error_report("Failed to use socket activation: %s",
Paolo Bonzini 0fb2b2
+                         error_get_pretty(local_err));
Paolo Bonzini 0fb2b2
+            exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        socket_path = NULL;
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (qemu_init_main_loop(&local_err)) {
Paolo Bonzini 0fb2b2
+        error_report_err(local_err);
Paolo Bonzini 0fb2b2
+        exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
Paolo Bonzini 0fb2b2
+                                         G_IO_IN,
Paolo Bonzini 0fb2b2
+                                         accept_client,
Paolo Bonzini 0fb2b2
+                                         NULL, NULL);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+#ifdef CONFIG_LIBCAP
Paolo Bonzini 0fb2b2
+    if (drop_privileges() < 0) {
Paolo Bonzini 0fb2b2
+        error_report("Failed to drop privileges: %s", strerror(errno));
Paolo Bonzini 0fb2b2
+        exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+#endif
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    if (daemonize) {
Paolo Bonzini 0fb2b2
+        if (daemon(0, 0) < 0) {
Paolo Bonzini 0fb2b2
+            error_report("Failed to daemonize: %s", strerror(errno));
Paolo Bonzini 0fb2b2
+            exit(EXIT_FAILURE);
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+        write_pidfile();
Paolo Bonzini 0fb2b2
+    }
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    state = RUNNING;
Paolo Bonzini 0fb2b2
+    do {
Paolo Bonzini 0fb2b2
+        main_loop_wait(false);
Paolo Bonzini 0fb2b2
+        if (state == TERMINATE) {
Paolo Bonzini 0fb2b2
+            state = TERMINATING;
Paolo Bonzini 0fb2b2
+            close_server_socket();
Paolo Bonzini 0fb2b2
+        }
Paolo Bonzini 0fb2b2
+    } while (num_active_sockets > 0);
Paolo Bonzini 0fb2b2
+
Paolo Bonzini 0fb2b2
+    exit(EXIT_SUCCESS);
Paolo Bonzini 0fb2b2
+}