Blame SOURCES/kvm-scsi-build-qemu-pr-helper.patch

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