ddd / rpms / qemu

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