summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan McDonald <danmcd@joyent.com>2022-04-25 08:23:27 -0400
committerDan McDonald <danmcd@joyent.com>2022-04-25 08:23:27 -0400
commitb7cc77ad34467677e66c1a06f3d717c4ea632bc5 (patch)
tree10934aa0fdb2ea01ada9896e382658d81634eb07
parent309baf317c94c37e7e9110980b39367afa97ec65 (diff)
parent1e6b83029f8d7ea1ade06314dc14e2fbd0cd2bcb (diff)
downloadillumos-joyent-b7cc77ad34467677e66c1a06f3d717c4ea632bc5.tar.gz
[illumos-gate merge]
commit 1e6b83029f8d7ea1ade06314dc14e2fbd0cd2bcb 14633 lib9p: unlinkat() does not work on 9p share commit 104fd2955714fa4ea6ed431b6b5cf2eb0766ecad 14623 bhyve/xhci: Connecting device on port 1 failed commit 77570342d4bf7e1e439bf1b8008312de7f031a0a 14653 loader: zfs: handle holes at the tail end correctly commit 1aa1f41fe10c6220a7fbef328fac1b72a8355a01 14495 bhyve VNC server should support alternate pixel formats 14494 bhyve VNC server could support multiple connections 14505 libumem's umem_update thread could be named 14506 libidspace does not expose id_allocff() 14640 bhyve VNC authentication fails with openssl3 Conflicts: usr/src/lib/libidspace/common/mapfile-vers usr/src/cmd/bhyve/Makefile
-rw-r--r--exception_lists/copyright1
-rw-r--r--exception_lists/cstyle1
-rw-r--r--exception_lists/hdrchk1
-rw-r--r--exception_lists/wscheck3
-rw-r--r--usr/src/boot/Makefile.version2
-rw-r--r--usr/src/boot/libsa/zfs/zfsimpl.c13
-rw-r--r--usr/src/cmd/bhyve/Makefile11
-rw-r--r--usr/src/cmd/bhyve/bhyvegc.c20
-rw-r--r--usr/src/cmd/bhyve/bhyvegc.h7
-rw-r--r--usr/src/cmd/bhyve/bhyverun.c2
-rw-r--r--usr/src/cmd/bhyve/pci_fbuf.c6
-rw-r--r--usr/src/cmd/bhyve/pci_xhci.c10
-rw-r--r--usr/src/cmd/bhyve/rfb.c2301
-rw-r--r--usr/src/cmd/bhyve/rfb.h12
-rw-r--r--usr/src/cmd/bhyve/rfb_impl.h252
-rw-r--r--usr/src/lib/lib9p/common/backend/fs.c10
-rw-r--r--usr/src/lib/lib9p/common/request.c6
-rw-r--r--usr/src/lib/libidspace/common/libidspace.h2
-rw-r--r--usr/src/lib/libidspace/common/mapfile-vers4
-rw-r--r--usr/src/lib/libumem/common/umem_update_thread.c1
-rw-r--r--usr/src/man/man5/bhyve_config.512
21 files changed, 1583 insertions, 1094 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright
index 12e0280517..408cdcf7e8 100644
--- a/exception_lists/copyright
+++ b/exception_lists/copyright
@@ -549,7 +549,6 @@ usr/src/cmd/bhyve/pmtmr.c
usr/src/cmd/bhyve/post.c
usr/src/cmd/bhyve/ps2kbd.[ch]
usr/src/cmd/bhyve/ps2mouse.[ch]
-usr/src/cmd/bhyve/rfb.[ch]
usr/src/cmd/bhyve/rtc.[ch]
usr/src/cmd/bhyve/smbiostbl.[ch]
usr/src/cmd/bhyve/sockstream.[ch]
diff --git a/exception_lists/cstyle b/exception_lists/cstyle
index 21cd3dd186..3e3f817bce 100644
--- a/exception_lists/cstyle
+++ b/exception_lists/cstyle
@@ -1403,7 +1403,6 @@ usr/src/cmd/bhyve/pm.c
usr/src/cmd/bhyve/post.c
usr/src/cmd/bhyve/ps2kbd.[ch]
usr/src/cmd/bhyve/ps2mouse.[ch]
-usr/src/cmd/bhyve/rfb.[ch]
usr/src/cmd/bhyve/rtc.[ch]
usr/src/cmd/bhyve/smbiostbl.[ch]
usr/src/cmd/bhyve/sockstream.[ch]
diff --git a/exception_lists/hdrchk b/exception_lists/hdrchk
index bf33421d86..7c0313395e 100644
--- a/exception_lists/hdrchk
+++ b/exception_lists/hdrchk
@@ -419,7 +419,6 @@ usr/src/cmd/bhyve/pci_lpc.h
usr/src/cmd/bhyve/pctestdev.h
usr/src/cmd/bhyve/ps2kbd.h
usr/src/cmd/bhyve/ps2mouse.h
-usr/src/cmd/bhyve/rfb.h
usr/src/cmd/bhyve/rtc.h
usr/src/cmd/bhyve/smbiostbl.h
usr/src/cmd/bhyve/sockstream.h
diff --git a/exception_lists/wscheck b/exception_lists/wscheck
index 90e43bba23..deb2697cdc 100644
--- a/exception_lists/wscheck
+++ b/exception_lists/wscheck
@@ -10,7 +10,7 @@
#
# Copyright 2019 Joyent, Inc.
# Copyright 2020 Oxide Computer Company
-# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
#
syntax: glob
@@ -171,7 +171,6 @@ usr/src/cmd/bhyve/pmtmr.c
usr/src/cmd/bhyve/post.c
usr/src/cmd/bhyve/ps2kbd.[ch]
usr/src/cmd/bhyve/ps2mouse.[ch]
-usr/src/cmd/bhyve/rfb.[ch]
usr/src/cmd/bhyve/rtc.[ch]
usr/src/cmd/bhyve/smbiostbl.[ch]
usr/src/cmd/bhyve/sockstream.[ch]
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version
index 6e0fa00d86..f0ea00a876 100644
--- a/usr/src/boot/Makefile.version
+++ b/usr/src/boot/Makefile.version
@@ -34,4 +34,4 @@ LOADER_VERSION = 1.1
# Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes.
# The version is processed from left to right, the version number can only
# be increased.
-BOOT_VERSION = $(LOADER_VERSION)-2022.01.25.1
+BOOT_VERSION = $(LOADER_VERSION)-2022.04.21.1
diff --git a/usr/src/boot/libsa/zfs/zfsimpl.c b/usr/src/boot/libsa/zfs/zfsimpl.c
index e83a8a3983..f2c5ca82d6 100644
--- a/usr/src/boot/libsa/zfs/zfsimpl.c
+++ b/usr/src/boot/libsa/zfs/zfsimpl.c
@@ -2348,6 +2348,19 @@ dnode_read(const spa_t *spa, const dnode_phys_t *dnode, off_t offset,
}
/*
+ * Handle odd block sizes, mirrors dmu_read_impl(). Data can't exist
+ * past the first block, so we'll clip the read to the portion of the
+ * buffer within bsize and zero out the remainder.
+ */
+ if (dnode->dn_maxblkid == 0) {
+ size_t newbuflen;
+
+ newbuflen = offset > bsize ? 0 : MIN(buflen, bsize - offset);
+ bzero((char *)buf + newbuflen, buflen - newbuflen);
+ buflen = newbuflen;
+ }
+
+ /*
* Note: bsize may not be a power of two here so we need to do an
* actual divide rather than a bitshift.
*/
diff --git a/usr/src/cmd/bhyve/Makefile b/usr/src/cmd/bhyve/Makefile
index e006bc3434..6de5407015 100644
--- a/usr/src/cmd/bhyve/Makefile
+++ b/usr/src/cmd/bhyve/Makefile
@@ -146,18 +146,23 @@ pci_xhci.o := CERRWARN += -_gcc11=-Wno-address-of-packed-member
SMOFF += all_func_returns,leaks,no_if_block
+rfb.o := SMOFF=
+
# Force c99 for everything
CSTD= $(CSTD_GNU99)
$(PROG) := LDLIBS += \
-l9p \
- -lsocket \
- -lnsl \
- -ldlpi \
+ -lcmdutils \
-ldladm \
+ -ldlpi \
+ -lidspace \
-lmd \
+ -lnsl \
-lnvpair \
+ -lsocket \
-lsunw_crypto \
+ -lumem \
-luuid \
-lvmmapi \
-lz
diff --git a/usr/src/cmd/bhyve/bhyvegc.c b/usr/src/cmd/bhyve/bhyvegc.c
index 4bd49ded79..3aa3bac210 100644
--- a/usr/src/cmd/bhyve/bhyvegc.c
+++ b/usr/src/cmd/bhyve/bhyvegc.c
@@ -35,6 +35,10 @@ __FBSDID("$FreeBSD$");
#include <stdio.h>
#include <string.h>
+#ifndef __FreeBSD__
+#include <pthread.h>
+#endif
+
#include "bhyvegc.h"
struct bhyvegc {
@@ -63,16 +67,26 @@ bhyvegc_init(int width, int height, void *fbaddr)
gc->gc_image = gc_image;
+#ifndef __FreeBSD__
+ pthread_mutex_init(&gc_image->mtx, NULL);
+#endif
+
return (gc);
}
void
bhyvegc_set_fbaddr(struct bhyvegc *gc, void *fbaddr)
{
+#ifndef __FreeBSD__
+ pthread_mutex_lock(&gc->gc_image->mtx);
+#endif
gc->raw = 1;
if (gc->gc_image->data && gc->gc_image->data != fbaddr)
free(gc->gc_image->data);
gc->gc_image->data = fbaddr;
+#ifndef __FreeBSD__
+ pthread_mutex_unlock(&gc->gc_image->mtx);
+#endif
}
void
@@ -80,6 +94,9 @@ bhyvegc_resize(struct bhyvegc *gc, int width, int height)
{
struct bhyvegc_image *gc_image;
+#ifndef __FreeBSD__
+ pthread_mutex_lock(&gc->gc_image->mtx);
+#endif
gc_image = gc->gc_image;
gc_image->width = width;
@@ -91,6 +108,9 @@ bhyvegc_resize(struct bhyvegc *gc, int width, int height)
memset(gc_image->data, 0, width * height *
sizeof (uint32_t));
}
+#ifndef __FreeBSD__
+ pthread_mutex_unlock(&gc->gc_image->mtx);
+#endif
}
struct bhyvegc_image *
diff --git a/usr/src/cmd/bhyve/bhyvegc.h b/usr/src/cmd/bhyve/bhyvegc.h
index 11323586df..0b68bfad9d 100644
--- a/usr/src/cmd/bhyve/bhyvegc.h
+++ b/usr/src/cmd/bhyve/bhyvegc.h
@@ -31,6 +31,10 @@
#ifndef _BHYVEGC_H_
#define _BHYVEGC_H_
+#ifndef __FreeBSD__
+#include <pthread.h>
+#endif
+
struct bhyvegc;
struct bhyvegc_image {
@@ -38,6 +42,9 @@ struct bhyvegc_image {
int width;
int height;
uint32_t *data;
+#ifndef __FreeBSD__
+ pthread_mutex_t mtx;
+#endif
};
struct bhyvegc *bhyvegc_init(int width, int height, void *fbaddr);
diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c
index a6819efb97..3b026f22a0 100644
--- a/usr/src/cmd/bhyve/bhyverun.c
+++ b/usr/src/cmd/bhyve/bhyverun.c
@@ -1692,8 +1692,6 @@ main(int argc, char *argv[])
}
#endif
- vga_init(1);
-
if (lpc_bootrom()) {
#ifdef __FreeBSD__
if (vm_set_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, 1)) {
diff --git a/usr/src/cmd/bhyve/pci_fbuf.c b/usr/src/cmd/bhyve/pci_fbuf.c
index fad064ebf8..5a17b1e618 100644
--- a/usr/src/cmd/bhyve/pci_fbuf.c
+++ b/usr/src/cmd/bhyve/pci_fbuf.c
@@ -434,6 +434,10 @@ pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
/* XXX until VGA rendering is enabled */
if (sc->vga_full != 0) {
EPRINTLN("pci_fbuf: VGA rendering not enabled");
+#ifndef __FreeBSD__
+ errno = ENOTSUP;
+ error = -1;
+#endif
goto done;
}
@@ -459,7 +463,7 @@ pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
(void) asprintf(&name, "%s (bhyve)", get_config_value("name"));
if (sc->rfb_unix != NULL) {
- error = rfb_init_unix(sc->rfb_unix, sc->rfb_wait,
+ error = rfb_init((char *)sc->rfb_unix, -1, sc->rfb_wait,
sc->rfb_password, name != NULL ? name : "bhyve");
} else {
error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait,
diff --git a/usr/src/cmd/bhyve/pci_xhci.c b/usr/src/cmd/bhyve/pci_xhci.c
index 97ce582772..0b9fde3208 100644
--- a/usr/src/cmd/bhyve/pci_xhci.c
+++ b/usr/src/cmd/bhyve/pci_xhci.c
@@ -483,8 +483,13 @@ pci_xhci_portregs_write(struct pci_xhci_softc *sc, uint64_t offset,
oldpls = XHCI_PS_PLS_GET(p->portsc);
newpls = XHCI_PS_PLS_GET(value);
+#ifndef __FreeBSD__
+ p->portsc &= XHCI_PS_PED | XHCI_PS_PP | XHCI_PS_PLS_MASK |
+ XHCI_PS_SPEED_MASK | XHCI_PS_PIC_MASK;
+#else
p->portsc &= XHCI_PS_PED | XHCI_PS_PLS_MASK |
XHCI_PS_SPEED_MASK | XHCI_PS_PIC_MASK;
+#endif
if (XHCI_DEVINST_PTR(sc, port))
p->portsc |= XHCI_PS_CCS;
@@ -2852,6 +2857,11 @@ pci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
struct pci_xhci_softc *sc;
int error;
+#ifndef __FreeBSD__
+ if (get_config_bool_default("xhci.debug", false))
+ xhci_debug = 1;
+#endif
+
if (xhci_in_use) {
WPRINTF(("pci_xhci controller already defined"));
return (-1);
diff --git a/usr/src/cmd/bhyve/rfb.c b/usr/src/cmd/bhyve/rfb.c
index 86b0d6d456..ab209aae36 100644
--- a/usr/src/cmd/bhyve/rfb.c
+++ b/usr/src/cmd/bhyve/rfb.c
@@ -4,7 +4,6 @@
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2015 Leon Dang
* Copyright 2020 Joyent, Inc.
- * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,1353 +27,1523 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
+ */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#ifndef WITHOUT_CAPSICUM
-#include <sys/capsicum.h>
-#endif
-#include <sys/endian.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <stdatomic.h>
-#include <machine/cpufunc.h>
-#include <machine/specialreg.h>
-#include <netinet/in.h>
-#include <netdb.h>
+/*
+ * References to the RFB protocol specification refer to:
+ * - [1] https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst
+ */
-#include <assert.h>
-#ifndef WITHOUT_CAPSICUM
-#include <capsicum_helpers.h>
-#endif
#include <err.h>
#include <errno.h>
+#include <libidspace.h>
+#include <netdb.h>
#include <pthread.h>
#include <pthread_np.h>
#include <signal.h>
+#include <stdatomic.h>
#include <stdbool.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <sysexits.h>
#include <unistd.h>
-
#include <zlib.h>
-
-#ifndef __FreeBSD__
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+#include <netinet/in.h>
+#ifndef NO_OPENSSL
+#include <openssl/des.h>
+#endif
#include <sys/debug.h>
+#include <sys/endian.h>
+#include <sys/list.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sysexits.h>
+#include <sys/capsicum.h>
+#include <capsicum_helpers.h>
#endif
#include "bhyvegc.h"
+#include "config.h"
#include "debug.h"
#include "console.h"
#include "rfb.h"
+#include "rfb_impl.h"
#include "sockstream.h"
-#ifndef NO_OPENSSL
-#include <openssl/des.h>
-#endif
+static uint_t rfb_debug = 0;
+static list_t rfb_list;
+static id_space_t *rfb_idspace;
+
+static bool rfb_sse42;
+static pthread_once_t rfb_once = PTHREAD_ONCE_INIT;
+
+extern int raw_stdio;
+
+static void rfb_send_extended_keyevent_update_msg(rfb_client_t *);
+
+static void
+rfb_printf(rfb_client_t *c, rfb_loglevel_t level, const char *fmt, ...)
+{
+ FILE *fp = stdout;
+ va_list ap;
+
+ switch (level) {
+ case RFB_LOGDEBUG:
+ if (rfb_debug == 0)
+ return;
+ /* FALLTHROUGH */
+ case RFB_LOGERR:
+ fp = stderr;
+ /* FALLTHROUGH */
+ case RFB_LOGWARN:
+ if (c != NULL)
+ (void) fprintf(fp, "rfb%u: ", c->rc_instance);
+ else
+ (void) fprintf(fp, "rfb: ");
+ va_start(ap, fmt);
+ (void) vfprintf(fp, fmt, ap);
+ va_end(ap);
+ if (raw_stdio)
+ (void) fprintf(fp, "\r\n");
+ else
+ (void) fprintf(fp, "\n");
+ (void) fflush(fp);
+ }
+}
+
+static void
+rfb_init_once(void)
+{
+ uint_t cpu_registers[4], ecx;
+
+ do_cpuid(1, cpu_registers);
+ ecx = cpu_registers[2];
+ rfb_sse42 = (ecx & CPUID2_SSE42) != 0;
+
+ if (rfb_sse42)
+ rfb_printf(NULL, RFB_LOGDEBUG, "enabled fast crc32");
+ else
+ rfb_printf(NULL, RFB_LOGWARN, "no support for fast crc32");
+
+ if (get_config_bool_default("rfb.debug", false))
+ rfb_debug = 1;
+
+ list_create(&rfb_list, sizeof (rfb_server_t),
+ offsetof(rfb_server_t, rs_node));
+
+ rfb_idspace = id_space_create("rfb", 0, INT32_MAX);
+}
+
+static void
+rfb_free_client(rfb_client_t *c)
+{
+ free(c->rc_crc);
+ free(c->rc_crc_tmp);
+ free(c->rc_zbuf);
+ free(c->rc_gci.data);
+
+ if (c->rc_encodings & RFB_ENCODING_ZLIB)
+ (void) deflateEnd(&c->rc_zstream);
+
+ if (c->rc_fd != -1)
+ (void) close(c->rc_fd);
+
+ free(c);
+}
-/* Delays in microseconds */
-#define CFD_SEL_DELAY 10000
-#define SCREEN_REFRESH_DELAY 33300 /* 30Hz */
-#define SCREEN_POLL_DELAY (SCREEN_REFRESH_DELAY / 2)
+/*
+ * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
+ */
+static inline uint32_t
+fast_crc32(void *buf, int len, uint32_t crcval)
+{
+ uint32_t q = len / sizeof (uint32_t);
+ uint32_t *p = (uint32_t *)buf;
-static int rfb_debug = 0;
-#define DPRINTF(params) if (rfb_debug) PRINTLN params
-#define WPRINTF(params) PRINTLN params
+ while (q--) {
+ /* BEGIN CSTYLED */
+ asm volatile (
+ /* crc32l %ecx,%esi */
+ ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
+ :"=S" (crcval)
+ :"0" (crcval), "c" (*p)
+ );
+ /* END CSTYLED */
+ p++;
+ }
+
+ return (crcval);
+}
-#define VERSION_LENGTH 12
-#define AUTH_LENGTH 16
-#define PASSWD_LENGTH 8
+static void
+rfb_send_client_status(rfb_client_t *c, uint32_t status, const char *msg)
+{
+ status = htonl(status);
-/* Protocol versions */
-#define CVERS_3_3 '3'
-#define CVERS_3_7 '7'
-#define CVERS_3_8 '8'
+ (void) stream_write(c->rc_fd, &status, sizeof (status));
-/* Client-to-server msg types */
-#define CS_SET_PIXEL_FORMAT 0
-#define CS_SET_ENCODINGS 2
-#define CS_UPDATE_MSG 3
-#define CS_KEY_EVENT 4
-#define CS_POINTER_EVENT 5
-#define CS_CUT_TEXT 6
-#define CS_MSG_CLIENT_QEMU 255
+ if (msg != NULL && status != 0 && c->rc_cver == RFB_CVER_3_8) {
+ char buf[4];
-#define SECURITY_TYPE_NONE 1
-#define SECURITY_TYPE_VNC_AUTH 2
+ rfb_printf(c, RFB_LOGWARN, msg);
-#define AUTH_FAILED_UNAUTH 1
-#define AUTH_FAILED_ERROR 2
+ be32enc(buf, strlen((char *)msg));
+ (void) stream_write(c->rc_fd, buf, 4);
+ (void) stream_write(c->rc_fd, msg, strlen((char *)msg));
+ }
+}
-struct rfb_softc {
- int sfd;
- pthread_t tid;
+static bool
+rfb_handshake_version(rfb_client_t *c)
+{
+ unsigned char buf[RFB_VERSION_LEN];
+ ssize_t l;
- int cfd;
+ rfb_printf(c, RFB_LOGDEBUG, "handshake version");
- int width, height;
+ if (stream_write(c->rc_fd, RFB_VERSION, RFB_VERSION_LEN) !=
+ RFB_VERSION_LEN) {
+ rfb_printf(c, RFB_LOGWARN, "could not send server version.");
+ return (false);
+ }
- char *password;
+ l = stream_read(c->rc_fd, buf, sizeof (buf));
+ if (l <= 0) {
+ rfb_printf(c, RFB_LOGWARN, "client version not read");
+ return (false);
+ } else if (l != RFB_VERSION_LEN) {
+ rfb_printf(c, RFB_LOGWARN, "client sent short version - '%.*s'",
+ l, buf);
+ return (false);
+ }
- bool enc_raw_ok;
- bool enc_zlib_ok;
- bool enc_resize_ok;
- bool enc_extkeyevent_ok;
+ rfb_printf(c, RFB_LOGDEBUG, "version handshake, client ver '%.*s'",
+ l - 1, buf);
- bool enc_extkeyevent_send;
+ if (strncmp(RFB_VERSION, (char *)buf, RFB_VERSION_LEN - 2) != 0) {
+ rfb_printf(c, RFB_LOGERR, "bad client version '%.*s'", l, buf);
+ return (false);
+ }
- z_stream zstream;
- uint8_t *zbuf;
- int zbuflen;
+ switch (buf[RFB_VERSION_LEN - 2]) {
+ case '8':
+ c->rc_cver = RFB_CVER_3_8;
+ break;
+ case '7':
+ c->rc_cver = RFB_CVER_3_7;
+ break;
+ case '5':
+ /*
+ * From the RFB specification[1], section 7.1.1:
+ * "version 3.5 was wrongly reported by some clients, but this
+ * should be interpreted by all servers as 3.3."
+ */
+ case '3':
+ c->rc_cver = RFB_CVER_3_3;
+ break;
+ default:
+ rfb_printf(c, RFB_LOGERR, "unsupported client version '%.*s'",
+ l - 1, buf);
+ return (false);
+ }
- int conn_wait;
- int wrcount;
+ return (true);
+}
- atomic_bool sending;
- atomic_bool pending;
- atomic_bool update_all;
- atomic_bool input_detected;
+static bool
+rfb_handshake_auth(rfb_client_t *c)
+{
+ unsigned char buf[RFBP_SECURITY_VNC_AUTH_LEN];
+ int auth_type;
- pthread_mutex_t mtx;
- pthread_cond_t cond;
+ rfb_printf(c, RFB_LOGDEBUG, "handshake auth");
- int hw_crc;
- uint32_t *crc; /* WxH crc cells */
- uint32_t *crc_tmp; /* buffer to store single crc row */
- int crc_width, crc_height;
-#ifndef __FreeBSD__
- const char *name;
+ auth_type = RFBP_SECURITY_NONE;
+#ifndef NO_OPENSSL
+ if (c->rc_s->rs_password != NULL)
+ auth_type = RFBP_SECURITY_VNC_AUTH;
#endif
-};
-
-struct rfb_pixfmt {
- uint8_t bpp;
- uint8_t depth;
- uint8_t bigendian;
- uint8_t truecolor;
- uint16_t red_max;
- uint16_t green_max;
- uint16_t blue_max;
- uint8_t red_shift;
- uint8_t green_shift;
- uint8_t blue_shift;
- uint8_t pad[3];
-};
-
-struct rfb_srvr_info {
- uint16_t width;
- uint16_t height;
- struct rfb_pixfmt pixfmt;
- uint32_t namelen;
-};
-
-struct rfb_pixfmt_msg {
- uint8_t type;
- uint8_t pad[3];
- struct rfb_pixfmt pixfmt;
-};
-
-#define RFB_ENCODING_RAW 0
-#define RFB_ENCODING_ZLIB 6
-#define RFB_ENCODING_RESIZE -223
-#define RFB_ENCODING_EXT_KEYEVENT -258
-
-#define RFB_CLIENTMSG_EXT_KEYEVENT 0
-
-#define RFB_MAX_WIDTH 2000
-#define RFB_MAX_HEIGHT 1200
-#define RFB_ZLIB_BUFSZ RFB_MAX_WIDTH*RFB_MAX_HEIGHT*4
-
-/* percentage changes to screen before sending the entire screen */
-#define RFB_SEND_ALL_THRESH 25
-
-struct rfb_enc_msg {
- uint8_t type;
- uint8_t pad;
- uint16_t numencs;
-};
-
-struct rfb_updt_msg {
- uint8_t type;
- uint8_t incremental;
- uint16_t x;
- uint16_t y;
- uint16_t width;
- uint16_t height;
-};
-
-struct rfb_key_msg {
- uint8_t type;
- uint8_t down;
- uint16_t pad;
- uint32_t sym;
-};
-
-struct rfb_client_msg {
- uint8_t type;
- uint8_t subtype;
-};
-
-struct rfb_extended_key_msg {
- uint8_t type;
- uint8_t subtype;
- uint16_t down;
- uint32_t sym;
- uint32_t code;
-};
-
-struct rfb_ptr_msg {
- uint8_t type;
- uint8_t button;
- uint16_t x;
- uint16_t y;
-};
-
-struct rfb_srvr_updt_msg {
- uint8_t type;
- uint8_t pad;
- uint16_t numrects;
-};
-
-struct rfb_srvr_rect_hdr {
- uint16_t x;
- uint16_t y;
- uint16_t width;
- uint16_t height;
- uint32_t encoding;
-};
-
-struct rfb_cuttext_msg {
- uint8_t type;
- uint8_t padding[3];
- uint32_t length;
-};
-static void
-#ifndef __FreeBSD__
-rfb_send_server_init_msg(int cfd, struct rfb_softc *rc)
+ switch (c->rc_cver) {
+ case RFB_CVER_3_3:
+ /*
+ * RFB specification[1] section 7.1.2:
+ * The server decides the security type and sends a single word.
+ */
+ be32enc(buf, auth_type);
+ (void) stream_write(c->rc_fd, buf, 4);
+
+ break;
+
+ case RFB_CVER_3_7:
+ case RFB_CVER_3_8:
+ /* Send list of supported types. */
+ buf[0] = 1; /* list length */
+ buf[1] = auth_type;
+ (void) stream_write(c->rc_fd, buf, 2);
+
+ /* Read agreed security type. */
+ if (stream_read(c->rc_fd, buf, 1) != 1) {
+ rfb_printf(c, RFB_LOGWARN,
+ "auth fail, no type from client");
+ return (false);
+ }
+
+ if (buf[0] != auth_type) {
+ rfb_send_client_status(c, 1,
+ "Auth failed: authentication type mismatch");
+ return (false);
+ }
+
+ break;
+ }
+
+ if (auth_type == RFBP_SECURITY_NONE) {
+ /*
+ * According to the RFB specification[1], section 7.2.1, for a
+ * security type of 'None', client versions 3.3 and 3.7 expect
+ * to move straight to the ClientInit phase, without the server
+ * sending a response. For version 3.8, a SecurityResult word
+ * needs to be sent indicating success.
+ */
+ switch (c->rc_cver) {
+ case RFB_CVER_3_3:
+ case RFB_CVER_3_7:
+ break;
+ case RFB_CVER_3_8:
+ rfb_send_client_status(c, 0, NULL);
+ break;
+ }
+ return (true);
+ }
+
+ /* Perform VNC authentication. */
+
+#ifdef NO_OPENSSL
+ rfb_printf(c, RFB_LOGERR,
+ "Auth not supported, no OpenSSL in your system");
+ rfb_send_client_status(c, 1, "Auth failed.");
+ return (false);
#else
-rfb_send_server_init_msg(int cfd)
+ unsigned char challenge[RFBP_SECURITY_VNC_AUTH_LEN];
+ unsigned char keystr[RFBP_SECURITY_VNC_PASSWD_LEN];
+ unsigned char crypt_expected[RFBP_SECURITY_VNC_AUTH_LEN];
+ DES_key_schedule ks;
+
+ /*
+ * The client encrypts the challenge with DES, using a password
+ * supplied by the user as the key.
+ * To form the key, the password is truncated to eight characters, or
+ * padded with null bytes on the right.
+ * The client then sends the resulting 16-bytes response.
+ */
+ (void) strncpy((char *)keystr, c->rc_s->rs_password,
+ RFBP_SECURITY_VNC_PASSWD_LEN);
+
+ /*
+ * VNC clients encrypt the challenge with all the bit fields in each
+ * byte of the password mirrored.
+ * Here we flip each byte of the keystr.
+ */
+ for (uint_t i = 0; i < RFBP_SECURITY_VNC_PASSWD_LEN; i++) {
+ keystr[i] = (keystr[i] & 0xf0) >> 4 | (keystr[i] & 0x0f) << 4;
+ keystr[i] = (keystr[i] & 0xcc) >> 2 | (keystr[i] & 0x33) << 2;
+ keystr[i] = (keystr[i] & 0xaa) >> 1 | (keystr[i] & 0x55) << 1;
+ }
+
+ /* Initialize a 16-byte random challenge. */
+ arc4random_buf(challenge, sizeof (challenge));
+ (void) stream_write(c->rc_fd, challenge, RFBP_SECURITY_VNC_AUTH_LEN);
+
+ /* Receive the 16-byte challenge response. */
+ if (stream_read(c->rc_fd, buf, RFBP_SECURITY_VNC_AUTH_LEN)
+ != RFBP_SECURITY_VNC_AUTH_LEN) {
+ rfb_send_client_status(c, 1, "response read failed");
+ return (false);
+ }
+
+ memcpy(crypt_expected, challenge, RFBP_SECURITY_VNC_AUTH_LEN);
+
+ /* Encrypt the Challenge with DES. */
+ DES_set_key_unchecked((const_DES_cblock *)keystr, &ks);
+ DES_ecb_encrypt((const_DES_cblock *)challenge,
+ (const_DES_cblock *)crypt_expected, &ks, DES_ENCRYPT);
+ DES_ecb_encrypt(
+ (const_DES_cblock *)(challenge + RFBP_SECURITY_VNC_PASSWD_LEN),
+ (const_DES_cblock *)(crypt_expected + RFBP_SECURITY_VNC_PASSWD_LEN),
+ &ks, DES_ENCRYPT);
+
+ if (memcmp(crypt_expected, buf, RFBP_SECURITY_VNC_AUTH_LEN) != 0) {
+ rfb_send_client_status(c, 1, "Auth failed: Invalid password.");
+ return (false);
+ }
+
+ rfb_printf(c, RFB_LOGDEBUG, "authentication succeeded");
+ rfb_send_client_status(c, 0, NULL);
#endif
+
+ return (true);
+}
+
+static bool
+rfb_handshake_init_message(rfb_client_t *c)
{
- struct bhyvegc_image *gc_image;
- struct rfb_srvr_info sinfo;
-
- gc_image = console_get_image();
-
- sinfo.width = htons(gc_image->width);
- sinfo.height = htons(gc_image->height);
- sinfo.pixfmt.bpp = 32;
- sinfo.pixfmt.depth = 32;
- sinfo.pixfmt.bigendian = 0;
- sinfo.pixfmt.truecolor = 1;
- sinfo.pixfmt.red_max = htons(255);
- sinfo.pixfmt.green_max = htons(255);
- sinfo.pixfmt.blue_max = htons(255);
- sinfo.pixfmt.red_shift = 16;
- sinfo.pixfmt.green_shift = 8;
- sinfo.pixfmt.blue_shift = 0;
- sinfo.pixfmt.pad[0] = 0;
- sinfo.pixfmt.pad[1] = 0;
- sinfo.pixfmt.pad[2] = 0;
-
-#ifndef __FreeBSD__
- const char *name = rc->name != NULL ? rc->name : "bhyve";
-
- sinfo.namelen = htonl(strlen(name));
- (void)stream_write(cfd, &sinfo, sizeof(sinfo));
- (void)stream_write(cfd, name, strlen(name));
-#else
- sinfo.namelen = htonl(strlen("bhyve"));
- (void)stream_write(cfd, &sinfo, sizeof(sinfo));
- (void)stream_write(cfd, "bhyve", strlen("bhyve"));
-#endif
+ struct bhyvegc_image *gci;
+ char buf[1];
+ char *name;
+
+ rfb_printf(c, RFB_LOGDEBUG, "handshake server init");
+
+ /* Read the client init message. */
+ if (stream_read(c->rc_fd, buf, 1) != 1) {
+ rfb_printf(c, RFB_LOGWARN, "client did not send init");
+ return (false);
+ }
+
+ if (buf[0] == 0) {
+ rfb_client_t *oc;
+
+ rfb_printf(c, RFB_LOGDEBUG,
+ "client requested exclusive access");
+
+ pthread_mutex_lock(&c->rc_s->rs_clientlock);
+ c->rc_s->rs_exclusive = true;
+ /* Disconnect all other clients. */
+ for (oc = list_head(&c->rc_s->rs_clients); oc != NULL;
+ oc = list_next(&c->rc_s->rs_clients, oc)) {
+ if (oc != c)
+ oc->rc_closing = true;
+ }
+ pthread_mutex_unlock(&c->rc_s->rs_clientlock);
+ } else {
+ rfb_printf(c, RFB_LOGDEBUG, "client requested shared access");
+
+ pthread_mutex_lock(&c->rc_s->rs_clientlock);
+ if (c->rc_s->rs_exclusive) {
+ rfb_printf(c, RFB_LOGWARN,
+ "deny due to existing exclusive session");
+ pthread_mutex_unlock(&c->rc_s->rs_clientlock);
+ return (false);
+ }
+ pthread_mutex_unlock(&c->rc_s->rs_clientlock);
+ }
+
+ gci = console_get_image();
+
+ c->rc_sinfo.rsi_width = htons(gci->width);
+ c->rc_sinfo.rsi_height = htons(gci->height);
+ c->rc_width = gci->width;
+ c->rc_height = gci->height;
+
+ if (c->rc_s->rs_name != NULL)
+ name = (char *)c->rc_s->rs_name;
+ else
+ name = "bhyve";
+
+ c->rc_sinfo.rsi_namelen = htonl(strlen(name));
+ (void) stream_write(c->rc_fd, &c->rc_sinfo, sizeof (c->rc_sinfo));
+ (void) stream_write(c->rc_fd, name, strlen(name));
+
+ return (true);
}
-static void
-rfb_send_resize_update_msg(struct rfb_softc *rc, int cfd)
+static bool
+rfb_handshake(rfb_client_t *c)
{
- struct rfb_srvr_updt_msg supdt_msg;
- struct rfb_srvr_rect_hdr srect_hdr;
-
- /* Number of rectangles: 1 */
- supdt_msg.type = 0;
- supdt_msg.pad = 0;
- supdt_msg.numrects = htons(1);
- stream_write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
-
- /* Rectangle header */
- srect_hdr.x = htons(0);
- srect_hdr.y = htons(0);
- srect_hdr.width = htons(rc->width);
- srect_hdr.height = htons(rc->height);
- srect_hdr.encoding = htonl(RFB_ENCODING_RESIZE);
- stream_write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
+ if (!rfb_handshake_version(c))
+ return (false);
+
+ if (!rfb_handshake_auth(c))
+ return (false);
+
+ if (!rfb_handshake_init_message(c))
+ return (false);
+
+ return (true);
}
static void
-rfb_send_extended_keyevent_update_msg(struct rfb_softc *rc, int cfd)
+rfb_print_pixfmt(rfb_client_t *c, rfb_pixfmt_t *px, rfb_loglevel_t level)
{
- struct rfb_srvr_updt_msg supdt_msg;
- struct rfb_srvr_rect_hdr srect_hdr;
-
- /* Number of rectangles: 1 */
- supdt_msg.type = 0;
- supdt_msg.pad = 0;
- supdt_msg.numrects = htons(1);
- stream_write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
-
- /* Rectangle header */
- srect_hdr.x = htons(0);
- srect_hdr.y = htons(0);
- srect_hdr.width = htons(rc->width);
- srect_hdr.height = htons(rc->height);
- srect_hdr.encoding = htonl(RFB_ENCODING_EXT_KEYEVENT);
- stream_write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
+ rfb_printf(c, level, "%20s: %u", "bpp", px->rp_bpp);
+ rfb_printf(c, level, "%20s: %u", "depth", px->rp_depth);
+ rfb_printf(c, level, "%20s: %u", "bigendian", px->rp_bigendian);
+ rfb_printf(c, level, "%20s: %u", "truecolour", px->rp_truecolour);
+ rfb_printf(c, level, "%20s: %u", "r_max", ntohs(px->rp_r_max));
+ rfb_printf(c, level, "%20s: %u", "g_max", ntohs(px->rp_g_max));
+ rfb_printf(c, level, "%20s: %u", "b_max", ntohs(px->rp_b_max));
+ rfb_printf(c, level, "%20s: %u", "r_shift", px->rp_r_shift);
+ rfb_printf(c, level, "%20s: %u", "g_shift", px->rp_g_shift);
+ rfb_printf(c, level, "%20s: %u", "b_shift", px->rp_b_shift);
}
-static void
-rfb_recv_set_pixfmt_msg(struct rfb_softc *rc, int cfd)
+static bool
+rfb_recv_set_pixel_format(rfb_client_t *c)
{
- struct rfb_pixfmt_msg pixfmt_msg;
+ rfb_cs_pixfmt_msg_t msg;
+ rfb_pixfmt_t *newpx = &msg.rp_pixfmt;
+ rfb_pixfmt_t *oldpx = &c->rc_sinfo.rsi_pixfmt;
+ rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt;
- (void)stream_read(cfd, ((void *)&pixfmt_msg)+1, sizeof(pixfmt_msg)-1);
-}
+ rfb_printf(c, RFB_LOGDEBUG, "received pixel format");
+ if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
+ return (false);
-static void
-rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd)
+ /*
+ * The client has sent its desired pixel format. The protocol does not
+ * have a mechanism to reject this, we are supposed to just start using
+ * the requested format from the next update.
+ *
+ * At present, we can only support alternative rgb-shift values and
+ * will accept (and ignore) a new depth value.
+ */
+
+ if (oldpx->rp_bpp != newpx->rp_bpp ||
+ oldpx->rp_bigendian != newpx->rp_bigendian ||
+ oldpx->rp_truecolour != newpx->rp_truecolour ||
+ oldpx->rp_r_max != newpx->rp_r_max ||
+ oldpx->rp_g_max != newpx->rp_g_max ||
+ oldpx->rp_b_max != newpx->rp_b_max) {
+ rfb_printf(c, RFB_LOGWARN, "unsupported pixfmt from client");
+ rfb_print_pixfmt(c, newpx, RFB_LOGWARN);
+ return (false);
+ }
+
+ rfb_print_pixfmt(c, newpx, RFB_LOGDEBUG);
+
+ /* Check if the new shifts match the server's native values. */
+ if (newpx->rp_r_shift != spx->rp_r_shift ||
+ newpx->rp_g_shift != spx->rp_g_shift ||
+ newpx->rp_b_shift != spx->rp_b_shift) {
+ c->rc_custom_pixfmt = true;
+ rfb_printf(c, RFB_LOGDEBUG, "Using custom pixfmt");
+ } else {
+ c->rc_custom_pixfmt = false;
+ rfb_printf(c, RFB_LOGDEBUG, "Using native pixfmt");
+ }
+
+ c->rc_sinfo.rsi_pixfmt = msg.rp_pixfmt;
+ c->rc_crc_reset = true;
+
+ return (true);
+}
+
+static bool
+rfb_recv_set_encodings(rfb_client_t *c)
{
- struct rfb_enc_msg enc_msg;
- int i;
- uint32_t encoding;
+ rfb_cs_encodings_msg_t msg;
+
+ rfb_printf(c, RFB_LOGDEBUG, "received encodings");
+
+ if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
+ return (false);
+
+ msg.re_numencs = htons(msg.re_numencs);
+
+ rfb_printf(c, RFB_LOGDEBUG, "%d values", msg.re_numencs);
+
+ for (uint_t i = 0; i < msg.re_numencs; i++) {
+ uint32_t enc;
- (void)stream_read(cfd, ((void *)&enc_msg)+1, sizeof(enc_msg)-1);
+ if (stream_read(c->rc_fd, &enc, sizeof (enc)) != sizeof (enc))
+ return (false);
- for (i = 0; i < htons(enc_msg.numencs); i++) {
- (void)stream_read(cfd, &encoding, sizeof(encoding));
- switch (htonl(encoding)) {
- case RFB_ENCODING_RAW:
- rc->enc_raw_ok = true;
+ enc = htonl(enc);
+
+ switch (enc) {
+ case RFBP_ENCODING_RAW:
+ rfb_printf(c, RFB_LOGDEBUG,
+ "client supports raw encoding");
+ c->rc_encodings |= RFB_ENCODING_RAW;
break;
- case RFB_ENCODING_ZLIB:
- if (!rc->enc_zlib_ok) {
- deflateInit(&rc->zstream, Z_BEST_SPEED);
- rc->enc_zlib_ok = true;
+ case RFBP_ENCODING_ZLIB:
+ rfb_printf(c, RFB_LOGDEBUG,
+ "client supports zlib encoding");
+ if (!(c->rc_encodings & RFB_ENCODING_ZLIB)) {
+ if (deflateInit(&c->rc_zstream, Z_BEST_SPEED)
+ != Z_OK) {
+ return (false);
+ }
+ c->rc_encodings |= RFB_ENCODING_ZLIB;
}
break;
- case RFB_ENCODING_RESIZE:
- rc->enc_resize_ok = true;
+ case RFBP_ENCODING_RESIZE:
+ rfb_printf(c, RFB_LOGDEBUG, "client supports resize");
+ c->rc_encodings |= RFB_ENCODING_RESIZE;
break;
- case RFB_ENCODING_EXT_KEYEVENT:
- rc->enc_extkeyevent_ok = true;
+ case RFBP_ENCODING_EXT_KEVENT:
+ rfb_printf(c, RFB_LOGDEBUG,
+ "client supports ext key event");
+ c->rc_encodings |= RFB_ENCODING_EXT_KEVENT;
break;
+ case RFBP_ENCODING_DESKTOP_NAME:
+ rfb_printf(c, RFB_LOGDEBUG,
+ "client supports desktop name");
+ c->rc_encodings |= RFB_ENCODING_DESKTOP_NAME;
+ break;
+ default:
+ rfb_printf(c, RFB_LOGDEBUG,
+ "client supports encoding %d", (int32_t)enc);
}
}
+
+ return (true);
}
-/*
- * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
- */
-static __inline uint32_t
-fast_crc32(void *buf, int len, uint32_t crcval)
+static bool
+rfb_recv_update(rfb_client_t *c)
{
- uint32_t q = len / sizeof(uint32_t);
- uint32_t *p = (uint32_t *)buf;
+ rfb_cs_update_msg_t msg;
- while (q--) {
- asm volatile (
- ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
- :"=S" (crcval)
- :"0" (crcval), "c" (*p)
- );
- p++;
+ if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
+ return (false);
+
+ if (!c->rc_keyevent_sent &&
+ (c->rc_encodings & RFB_ENCODING_EXT_KEVENT)) {
+ /*
+ * Take this opportunity to tell the client that we
+ * accept QEMU Extended Key Event Pseudo-encoding.
+ */
+ c->rc_keyevent_sent = true;
+ rfb_send_extended_keyevent_update_msg(c);
}
- return (crcval);
+ c->rc_pending = true;
+ if (msg.rum_incremental == 0) {
+ rfb_printf(c, RFB_LOGDEBUG,
+ "client requested full screen update");
+ c->rc_send_fullscreen = true;
+ }
+
+ return (true);
+}
+
+static bool
+rfb_recv_key_event(rfb_client_t *c)
+{
+ rfb_cs_key_event_msg_t msg;
+
+ if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
+ return (false);
+
+ msg.rke_sym = htonl(msg.rke_sym);
+
+ rfb_printf(c, RFB_LOGDEBUG, "received key %s %x",
+ msg.rke_down == 0 ? "up" : "down", msg.rke_sym);
+
+ console_key_event(msg.rke_down, msg.rke_sym, htonl(0));
+ c->rc_input_detected = true;
+
+ return (true);
+}
+
+static bool
+rfb_recv_pointer_event(rfb_client_t *c)
+{
+ rfb_cs_pointer_event_msg_t msg;
+
+ if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
+ return (false);
+
+ msg.rpe_x = htons(msg.rpe_x);
+ msg.rpe_y = htons(msg.rpe_y);
+
+ if (rfb_debug > 1) {
+ rfb_printf(c, RFB_LOGDEBUG, "received pointer event @ %dx%d",
+ msg.rpe_x, msg.rpe_y);
+ }
+
+ console_ptr_event(msg.rpe_button, msg.rpe_x, msg.rpe_y);
+ c->rc_input_detected = true;
+
+ return (true);
}
-static int
-rfb_send_update_header(struct rfb_softc *rc, int cfd, int numrects)
+static bool
+rfb_recv_cut_text(rfb_client_t *c)
{
- struct rfb_srvr_updt_msg supdt_msg;
+ rfb_cs_cut_text_msg_t msg;
+ unsigned char buf[32];
- supdt_msg.type = 0;
- supdt_msg.pad = 0;
- supdt_msg.numrects = htons(numrects);
+ rfb_printf(c, RFB_LOGDEBUG, "received cut text event");
+
+ if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
+ return (false);
+
+ msg.rct_length = htonl(msg.rct_length);
+ rfb_printf(c, RFB_LOGDEBUG, "%u bytes in buffer", msg.rct_length);
+ /* Consume the buffer */
+ while (msg.rct_length > 0) {
+ ssize_t l;
+
+ l = stream_read(c->rc_fd, buf,
+ MIN(sizeof (buf), msg.rct_length));
+ if (l <= 0)
+ return (false);
+ msg.rct_length -= l;
+ }
- return stream_write(cfd, &supdt_msg,
- sizeof(struct rfb_srvr_updt_msg));
+ return (true);
}
-static int
-rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc,
- int x, int y, int w, int h)
+static bool
+rfb_recv_qemu(rfb_client_t *c)
{
- struct rfb_srvr_rect_hdr srect_hdr;
+ rfb_cs_qemu_msg_t msg;
+
+ rfb_printf(c, RFB_LOGDEBUG, "received QEMU event");
+
+ if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
+ return (false);
+
+ switch (msg.rq_subtype) {
+ case RFBP_CS_QEMU_KEVENT: {
+ rfb_cs_qemu_extended_key_msg_t keymsg;
+
+ if (stream_read(c->rc_fd, &keymsg, sizeof (keymsg)) !=
+ sizeof (keymsg)) {
+ return (false);
+ }
+
+ keymsg.rqek_sym = htonl(keymsg.rqek_sym);
+ keymsg.rqek_code = htonl(keymsg.rqek_code);
+
+ rfb_printf(c, RFB_LOGDEBUG, "QEMU key %s %x / %x",
+ keymsg.rqek_down == 0 ? "up" : "down",
+ keymsg.rqek_sym, keymsg.rqek_code);
+
+ console_key_event((int)keymsg.rqek_down, keymsg.rqek_sym,
+ keymsg.rqek_code);
+ c->rc_input_detected = true;
+ break;
+ }
+ default:
+ rfb_printf(c, RFB_LOGWARN, "Unknown QEMU event subtype: %d\n",
+ msg.rq_subtype);
+ return (false);
+ }
+
+ return (true);
+}
+
+static bool
+rfb_send_update_header(rfb_client_t *c, int numrects)
+{
+ rfb_server_update_msg_t msg;
+
+ msg.rss_type = RFBP_SC_UPDATE;
+ msg.rss_pad = 0;
+ msg.rss_numrects = htons(numrects);
+
+ return (stream_write(c->rc_fd, &msg, sizeof (msg)) == sizeof (msg));
+}
+
+static void
+rfb_send_resize_update_msg(rfb_client_t *c)
+{
+ rfb_rect_hdr_t rect;
+
+ rfb_printf(c, RFB_LOGDEBUG, "sending screen resize %dx%d",
+ c->rc_width, c->rc_height);
+
+ (void) rfb_send_update_header(c, 1);
+
+ rect.rr_x = htons(0);
+ rect.rr_y = htons(0);
+ rect.rr_width = htons(c->rc_width);
+ rect.rr_height = htons(c->rc_height);
+ rect.rr_encoding = htonl(RFBP_ENCODING_RESIZE);
+
+ (void) stream_write(c->rc_fd, &rect, sizeof (rect));
+}
+
+static void
+rfb_send_extended_keyevent_update_msg(rfb_client_t *c)
+{
+ rfb_rect_hdr_t rect;
+
+ rfb_printf(c, RFB_LOGDEBUG, "sending extended keyevent update message");
+
+ (void) rfb_send_update_header(c, 1);
+
+ rect.rr_x = htons(0);
+ rect.rr_y = htons(0);
+ rect.rr_width = htons(c->rc_width);
+ rect.rr_height = htons(c->rc_height);
+ rect.rr_encoding = htonl(RFBP_ENCODING_EXT_KEVENT);
+
+ (void) stream_write(c->rc_fd, &rect, sizeof (rect));
+}
+
+static void
+translate_pixels(rfb_client_t *c, struct bhyvegc_image *gci,
+ int x1, int y1, int x2, int y2)
+{
+ rfb_pixfmt_t *px = &c->rc_sinfo.rsi_pixfmt;
+ rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt;
+ int w, h;
+
+ w = gci->width;
+ h = gci->height;
+ VERIFY3S(gci->width, ==, c->rc_gci.width);
+ VERIFY3S(gci->height, ==, c->rc_gci.height);
+
+ for (uint_t y = y1; y < h && y < y2; y++) {
+ for (uint_t x = x1; x < w && x < x2; x++) {
+ uint32_t p;
+
+ p = gci->data[y * w + x];
+ c->rc_gci.data[y * w + x] =
+ 0xff000000 |
+ ((p >> spx->rp_r_shift) & 0xff) << px->rp_r_shift |
+ ((p >> spx->rp_g_shift) & 0xff) << px->rp_g_shift |
+ ((p >> spx->rp_b_shift) & 0xff) << px->rp_b_shift;
+ }
+ }
+}
+
+static bool
+rfb_send_rect(rfb_client_t *c, struct bhyvegc_image *gci,
+ int x, int y, int w, int h)
+{
+ rfb_rect_hdr_t rect;
unsigned long zlen;
ssize_t nwrite, total;
int err;
uint32_t *p;
uint8_t *zbufp;
- /*
- * Send a single rectangle of the given x, y, w h dimensions.
- */
+ if (rfb_debug > 1) {
+ rfb_printf(c, RFB_LOGDEBUG, "send rect %dx%d %dx%d",
+ x, y, w, h);
+ }
- /* Rectangle header */
- srect_hdr.x = htons(x);
- srect_hdr.y = htons(y);
- srect_hdr.width = htons(w);
- srect_hdr.height = htons(h);
+ /* Rectangle header. */
+ rect.rr_x = htons(x);
+ rect.rr_y = htons(y);
+ rect.rr_width = htons(w);
+ rect.rr_height = htons(h);
+
+ uint32_t *data = gci->data;
+ if (c->rc_custom_pixfmt) {
+ translate_pixels(c, gci, x, y, x + w, y + h);
+ data = c->rc_gci.data;
+ }
h = y + h;
- w *= sizeof(uint32_t);
- if (rc->enc_zlib_ok) {
- zbufp = rc->zbuf;
- rc->zstream.total_in = 0;
- rc->zstream.total_out = 0;
- for (p = &gc->data[y * gc->width + x]; y < h; y++) {
- rc->zstream.next_in = (Bytef *)p;
- rc->zstream.avail_in = w;
- rc->zstream.next_out = (Bytef *)zbufp;
- rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16 -
- rc->zstream.total_out;
- rc->zstream.data_type = Z_BINARY;
-
- /* Compress with zlib */
- err = deflate(&rc->zstream, Z_SYNC_FLUSH);
+ w *= sizeof (uint32_t);
+
+ if (c->rc_encodings & RFB_ENCODING_ZLIB) {
+ zbufp = c->rc_zbuf;
+ c->rc_zstream.total_in = 0;
+ c->rc_zstream.total_out = 0;
+ for (p = &data[y * gci->width + x]; y < h; y++) {
+ c->rc_zstream.next_in = (Bytef *)p;
+ c->rc_zstream.avail_in = w;
+ c->rc_zstream.next_out = (Bytef *)zbufp;
+ c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16 -
+ c->rc_zstream.total_out;
+ c->rc_zstream.data_type = Z_BINARY;
+
+ /* Compress with zlib. */
+ err = deflate(&c->rc_zstream, Z_SYNC_FLUSH);
if (err != Z_OK) {
- WPRINTF(("zlib[rect] deflate err: %d", err));
- rc->enc_zlib_ok = false;
- deflateEnd(&rc->zstream);
+ rfb_printf(c, RFB_LOGWARN,
+ "zlib[rect] deflate err: %d", err);
goto doraw;
}
- zbufp = rc->zbuf + rc->zstream.total_out;
- p += gc->width;
+ zbufp = c->rc_zbuf + c->rc_zstream.total_out;
+ p += gci->width;
}
- srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
- nwrite = stream_write(cfd, &srect_hdr,
- sizeof(struct rfb_srvr_rect_hdr));
+ rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB);
+ nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
if (nwrite <= 0)
- return (nwrite);
+ return (false);
- zlen = htonl(rc->zstream.total_out);
- nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
+ zlen = htonl(c->rc_zstream.total_out);
+ nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t));
if (nwrite <= 0)
- return (nwrite);
- return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
+ return (false);
+ return (stream_write(c->rc_fd, c->rc_zbuf,
+ c->rc_zstream.total_out) == c->rc_zstream.total_out);
}
doraw:
total = 0;
- zbufp = rc->zbuf;
- for (p = &gc->data[y * gc->width + x]; y < h; y++) {
+ zbufp = c->rc_zbuf;
+ for (p = &data[y * gci->width + x]; y < h; y++) {
memcpy(zbufp, p, w);
zbufp += w;
total += w;
- p += gc->width;
+ p += gci->width;
}
- srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
- nwrite = stream_write(cfd, &srect_hdr,
- sizeof(struct rfb_srvr_rect_hdr));
+ rect.rr_encoding = htonl(RFBP_ENCODING_RAW);
+ nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
if (nwrite <= 0)
- return (nwrite);
+ return (false);
- total = stream_write(cfd, rc->zbuf, total);
-
- return (total);
+ return (stream_write(c->rc_fd, c->rc_zbuf, total) == total);
}
-static int
-rfb_send_all(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc)
+
+static bool
+rfb_send_all(rfb_client_t *c, struct bhyvegc_image *gci)
{
- struct rfb_srvr_updt_msg supdt_msg;
- struct rfb_srvr_rect_hdr srect_hdr;
+ rfb_rect_hdr_t rect;
ssize_t nwrite;
unsigned long zlen;
int err;
- /*
- * Send the whole thing
- */
+ rfb_printf(c, RFB_LOGDEBUG, "send entire screen");
- /* Number of rectangles: 1 */
- supdt_msg.type = 0;
- supdt_msg.pad = 0;
- supdt_msg.numrects = htons(1);
- nwrite = stream_write(cfd, &supdt_msg,
- sizeof(struct rfb_srvr_updt_msg));
- if (nwrite <= 0)
- return (nwrite);
-
- /* Rectangle header */
- srect_hdr.x = 0;
- srect_hdr.y = 0;
- srect_hdr.width = htons(gc->width);
- srect_hdr.height = htons(gc->height);
- if (rc->enc_zlib_ok) {
- rc->zstream.next_in = (Bytef *)gc->data;
- rc->zstream.avail_in = gc->width * gc->height *
- sizeof(uint32_t);
- rc->zstream.next_out = (Bytef *)rc->zbuf;
- rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16;
- rc->zstream.data_type = Z_BINARY;
-
- rc->zstream.total_in = 0;
- rc->zstream.total_out = 0;
-
- /* Compress with zlib */
- err = deflate(&rc->zstream, Z_SYNC_FLUSH);
+ /* Just the one (big) rect. */
+ if (!rfb_send_update_header(c, 1))
+ return (false);
+
+ rect.rr_x = 0;
+ rect.rr_y = 0;
+ rect.rr_width = htons(gci->width);
+ rect.rr_height = htons(gci->height);
+
+ uint32_t *data = gci->data;
+ if (c->rc_custom_pixfmt) {
+ translate_pixels(c, gci, 0, 0, gci->width, gci->height);
+ data = c->rc_gci.data;
+ }
+
+ if (c->rc_encodings & RFB_ENCODING_ZLIB) {
+ c->rc_zstream.next_in = (Bytef *)data;
+ c->rc_zstream.avail_in = gci->width * gci->height *
+ sizeof (uint32_t);
+ c->rc_zstream.next_out = (Bytef *)c->rc_zbuf;
+ c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16;
+ c->rc_zstream.data_type = Z_BINARY;
+
+ c->rc_zstream.total_in = 0;
+ c->rc_zstream.total_out = 0;
+
+ /* Compress with zlib. */
+ err = deflate(&c->rc_zstream, Z_SYNC_FLUSH);
if (err != Z_OK) {
- WPRINTF(("zlib deflate err: %d", err));
- rc->enc_zlib_ok = false;
- deflateEnd(&rc->zstream);
+ rfb_printf(c, RFB_LOGWARN, "zlib deflate err: %d", err);
goto doraw;
}
- srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
- nwrite = stream_write(cfd, &srect_hdr,
- sizeof(struct rfb_srvr_rect_hdr));
+ rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB);
+ nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
if (nwrite <= 0)
- return (nwrite);
+ return (false);
- zlen = htonl(rc->zstream.total_out);
- nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
+ zlen = htonl(c->rc_zstream.total_out);
+ nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t));
if (nwrite <= 0)
- return (nwrite);
- return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
+ return (false);
+ return (stream_write(c->rc_fd, c->rc_zbuf,
+ c->rc_zstream.total_out) == c->rc_zstream.total_out);
}
doraw:
- srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
- nwrite = stream_write(cfd, &srect_hdr,
- sizeof(struct rfb_srvr_rect_hdr));
+ rect.rr_encoding = htonl(RFBP_ENCODING_RAW);
+ nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
if (nwrite <= 0)
- return (nwrite);
+ return (false);
- nwrite = stream_write(cfd, gc->data,
- gc->width * gc->height * sizeof(uint32_t));
-
- return (nwrite);
+ nwrite = gci->width * gci->height * sizeof (uint32_t);
+ return (stream_write(c->rc_fd, data, nwrite) == nwrite);
}
-#define PIX_PER_CELL 32
-#define PIXCELL_SHIFT 5
-#define PIXCELL_MASK 0x1F
-
-static int
-rfb_send_screen(struct rfb_softc *rc, int cfd)
+static bool
+rfb_send_screen(rfb_client_t *c)
{
- struct bhyvegc_image *gc_image;
- ssize_t nwrite;
- int x, y;
- int celly, cellwidth;
+ struct bhyvegc_image *gci;
+ bool retval = true;
+ bool sendall = false;
int xcells, ycells;
- int w, h;
- uint32_t *p;
- int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */
- int retval;
- uint32_t *crc_p, *orig_crc;
- int changes;
- bool expected;
-
- /* Return if another thread sending */
- expected = false;
- if (atomic_compare_exchange_strong(&rc->sending, &expected, true) == false)
- return (1);
+ int rem_x, rem_y;
+ uint32_t *p, *ncrc, *ocrc;
+ uint_t changes, perc, x, y;
- retval = 1;
-
- /* Updates require a preceding update request */
- if (atomic_exchange(&rc->pending, false) == false)
- goto done;
+ /* Updates require a preceding client update request. */
+ if (atomic_exchange(&c->rc_pending, false) == false)
+ return (true);
console_refresh();
- gc_image = console_get_image();
-
- /* Clear old CRC values when the size changes */
- if (rc->crc_width != gc_image->width ||
- rc->crc_height != gc_image->height) {
- memset(rc->crc, 0, sizeof(uint32_t) *
- howmany(RFB_MAX_WIDTH, PIX_PER_CELL) *
- howmany(RFB_MAX_HEIGHT, PIX_PER_CELL));
- rc->crc_width = gc_image->width;
- rc->crc_height = gc_image->height;
- }
+ gci = console_get_image();
- /* A size update counts as an update in itself */
- if (rc->width != gc_image->width ||
- rc->height != gc_image->height) {
- rc->width = gc_image->width;
- rc->height = gc_image->height;
- if (rc->enc_resize_ok) {
- rfb_send_resize_update_msg(rc, cfd);
- rc->update_all = true;
+ /*
+ * It's helpful if the image size or data address does not change
+ * underneath us.
+ */
+ pthread_mutex_lock(&gci->mtx);
+
+ /* Check for screen resolution changes. */
+ if (c->rc_width != gci->width ||
+ c->rc_height != gci->height) {
+ c->rc_width = gci->width;
+ c->rc_height = gci->height;
+ c->rc_crc_reset = true;
+ c->rc_send_fullscreen = true;
+
+ /* If the client supports it, send a resize event. */
+ if (c->rc_encodings & RFB_ENCODING_RESIZE) {
+ rfb_send_resize_update_msg(c);
+ /*
+ * A resize message counts as an update in response to
+ * the client's preceding request so rc->pending does
+ * not need to be reset here.
+ */
goto done;
}
}
- if (atomic_exchange(&rc->update_all, false) == true) {
- retval = rfb_send_all(rc, cfd, gc_image);
- goto done;
+ /* Clear old CRC values. */
+ if (atomic_exchange(&c->rc_crc_reset, false))
+ memset(c->rc_crc, '\0', c->rc_cells * sizeof (uint32_t));
+
+ if (c->rc_custom_pixfmt && (c->rc_gci.data == NULL ||
+ c->rc_gci.width != c->rc_width ||
+ c->rc_gci.height != c->rc_height)) {
+ c->rc_gci.data = reallocarray(c->rc_gci.data,
+ c->rc_width * c->rc_height, sizeof (uint32_t));
+ if (c->rc_gci.data == NULL) {
+ retval = false;
+ goto done;
+ }
+ c->rc_gci.width = c->rc_width;
+ c->rc_gci.height = c->rc_height;
+ } else if (!c->rc_custom_pixfmt && c->rc_gci.data != NULL) {
+ free(c->rc_gci.data);
+ c->rc_gci.data = NULL;
}
+ sendall = atomic_exchange(&c->rc_send_fullscreen, false);
+
/*
- * Calculate the checksum for each 32x32 cell. Send each that
- * has changed since the last scan.
+ * Calculate a checksum for each 32x32 cell. Send all that have
+ * changed since the last scan.
*/
- w = rc->crc_width;
- h = rc->crc_height;
- xcells = howmany(rc->crc_width, PIX_PER_CELL);
- ycells = howmany(rc->crc_height, PIX_PER_CELL);
-
- rem_x = w & PIXCELL_MASK;
-
- rem_y = h & PIXCELL_MASK;
- if (!rem_y)
- rem_y = PIX_PER_CELL;
+ xcells = howmany(gci->width, RFB_PIX_PER_CELL);
+ ycells = howmany(gci->height, RFB_PIX_PER_CELL);
+ rem_x = gci->width & RFB_PIXCELL_MASK;
+ rem_y = gci->height & RFB_PIXCELL_MASK;
+ if (rem_y == 0)
+ rem_y = RFB_PIX_PER_CELL;
- p = gc_image->data;
+ p = gci->data;
- /*
- * Go through all cells and calculate crc. If significant number
- * of changes, then send entire screen.
- * crc_tmp is dual purpose: to store the new crc and to flag as
- * a cell that has changed.
- */
- crc_p = rc->crc_tmp - xcells;
- orig_crc = rc->crc - xcells;
+ ncrc = c->rc_crc_tmp - xcells;
+ ocrc = c->rc_crc - xcells;
changes = 0;
- memset(rc->crc_tmp, 0, sizeof(uint32_t) * xcells * ycells);
- for (y = 0; y < h; y++) {
- if ((y & PIXCELL_MASK) == 0) {
- crc_p += xcells;
- orig_crc += xcells;
+ memset(c->rc_crc_tmp, '\0', sizeof (uint32_t) * xcells * ycells);
+ for (y = 0; y < gci->height; y++) {
+ if ((y & RFB_PIXCELL_MASK) == 0) {
+ ncrc += xcells;
+ ocrc += xcells;
}
for (x = 0; x < xcells; x++) {
- if (x == (xcells - 1) && rem_x > 0)
- cellwidth = rem_x;
- else
- cellwidth = PIX_PER_CELL;
+ uint_t cellwidth;
- if (rc->hw_crc)
- crc_p[x] = fast_crc32(p,
- cellwidth * sizeof(uint32_t),
- crc_p[x]);
+ if (x == xcells - 1 && rem_x > 0)
+ cellwidth = rem_x;
else
- crc_p[x] = (uint32_t)crc32(crc_p[x],
- (Bytef *)p,
- cellwidth * sizeof(uint32_t));
+ cellwidth = RFB_PIX_PER_CELL;
+
+ if (rfb_sse42) {
+ ncrc[x] = fast_crc32(p,
+ cellwidth * sizeof (uint32_t), ncrc[x]);
+ } else {
+ ncrc[x] = (uint32_t)crc32(ncrc[x],
+ (Bytef *)p, cellwidth * sizeof (uint32_t));
+ }
p += cellwidth;
- /* check for crc delta if last row in cell */
- if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
- if (orig_crc[x] != crc_p[x]) {
- orig_crc[x] = crc_p[x];
- crc_p[x] = 1;
+ /* check for crc delta if last row in cell. */
+ if ((y & RFB_PIXCELL_MASK) == RFB_PIXCELL_MASK ||
+ y == gci->height - 1) {
+ if (ocrc[x] != ncrc[x]) {
+ ocrc[x] = ncrc[x];
+ ncrc[x] = 1;
changes++;
} else {
- crc_p[x] = 0;
+ ncrc[x] = 0;
}
}
}
}
- /*
- * We only send the update if there are changes.
- * Restore the pending flag since it was unconditionally cleared
- * above.
- */
- if (!changes) {
- rc->pending = true;
+ perc = (changes * 100) / (xcells * ycells);
+ if (rfb_debug > 1 && changes > 0) {
+ rfb_printf(c, RFB_LOGDEBUG,
+ "scanned and found %u changed cell(s) - %u%%",
+ changes, perc);
+ }
+
+ /*
+ * If there are no changes, don't send an update. Restore the pending
+ * flag since we still owe the client an update.
+ */
+ if (!sendall && !changes) {
+ c->rc_pending = true;
goto done;
}
- /* If number of changes is > THRESH percent, send the whole screen */
- if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) {
- retval = rfb_send_all(rc, cfd, gc_image);
+ /* If there are a lot of changes, send the whole screen. */
+ if (perc >= RFB_SENDALL_THRESH)
+ sendall = true;
+
+ if (sendall) {
+ retval = rfb_send_all(c, gci);
goto done;
}
- rfb_send_update_header(rc, cfd, changes);
+ if (!rfb_send_update_header(c, changes)) {
+ retval = false;
+ goto done;
+ }
- /* Go through all cells, and send only changed ones */
- crc_p = rc->crc_tmp;
- for (y = 0; y < h; y += PIX_PER_CELL) {
- /* previous cell's row */
- celly = (y >> PIXCELL_SHIFT);
+ /* Send the changed cells as separate rects. */
+ ncrc = c->rc_crc_tmp;
+ for (y = 0; y < gci->height; y += RFB_PIX_PER_CELL) {
+ /* Previous cell's row. */
+ int celly = (y >> RFB_PIXCELL_SHIFT);
- /* Delta check crc to previous set */
+ /* Delta check crc to previous set. */
for (x = 0; x < xcells; x++) {
- if (*crc_p++ == 0)
+ uint_t cellwidth;
+
+ if (*ncrc++ == 0)
continue;
- if (x == (xcells - 1) && rem_x > 0)
+ if (x == xcells - 1 && rem_x > 0)
cellwidth = rem_x;
else
- cellwidth = PIX_PER_CELL;
- nwrite = rfb_send_rect(rc, cfd,
- gc_image,
- x * PIX_PER_CELL,
- celly * PIX_PER_CELL,
- cellwidth,
- y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL);
- if (nwrite <= 0) {
- retval = nwrite;
+ cellwidth = RFB_PIX_PER_CELL;
+
+ if (!rfb_send_rect(c, gci,
+ x * RFB_PIX_PER_CELL, celly * RFB_PIX_PER_CELL,
+ cellwidth, y + RFB_PIX_PER_CELL >= gci->height ?
+ rem_y : RFB_PIX_PER_CELL)) {
+ retval = false;
goto done;
}
}
}
done:
- rc->sending = false;
+ pthread_mutex_unlock(&gci->mtx);
return (retval);
}
-
-static void
-rfb_recv_update_msg(struct rfb_softc *rc, int cfd)
+static void *
+rfb_client_rx_thread(void *arg)
{
- struct rfb_updt_msg updt_msg;
-
- (void)stream_read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1);
-
- if (rc->enc_extkeyevent_ok && (!rc->enc_extkeyevent_send)) {
- rfb_send_extended_keyevent_update_msg(rc, cfd);
- rc->enc_extkeyevent_send = true;
+ rfb_client_t *c = arg;
+ unsigned char cmd;
+ bool ret = true;
+
+ while (ret && !c->rc_closing && (read(c->rc_fd, &cmd, 1) == 1)) {
+ switch (cmd) {
+ case RFBP_CS_SET_PIXEL_FORMAT:
+ ret = rfb_recv_set_pixel_format(c);
+ break;
+ case RFBP_CS_SET_ENCODINGS:
+ ret = rfb_recv_set_encodings(c);
+ break;
+ case RFBP_CS_UPDATE_REQUEST:
+ ret = rfb_recv_update(c);
+ break;
+ case RFBP_CS_KEY_EVENT:
+ ret = rfb_recv_key_event(c);
+ break;
+ case RFBP_CS_POINTER_EVENT:
+ ret = rfb_recv_pointer_event(c);
+ break;
+ case RFBP_CS_CUT_TEXT:
+ ret = rfb_recv_cut_text(c);
+ break;
+ case RFBP_CS_QEMU:
+ ret = rfb_recv_qemu(c);
+ break;
+ default:
+ rfb_printf(c, RFB_LOGWARN, "unknown cs code %d",
+ cmd & 0xff);
+ ret = false;
+ }
}
- rc->pending = true;
- if (!updt_msg.incremental)
- rc->update_all = true;
-}
-
-static void
-rfb_recv_key_msg(struct rfb_softc *rc, int cfd)
-{
- struct rfb_key_msg key_msg;
+ rfb_printf(c, RFB_LOGDEBUG, "client rx thread exiting");
+ c->rc_closing = true;
- (void)stream_read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1);
-
- console_key_event(key_msg.down, htonl(key_msg.sym), htonl(0));
- rc->input_detected = true;
+ return (NULL);
}
-static void
-rfb_recv_client_msg(struct rfb_softc *rc, int cfd)
+static void *
+rfb_client_tx_thread(void *arg)
{
- struct rfb_client_msg client_msg;
- struct rfb_extended_key_msg extkey_msg;
-
- (void)stream_read(cfd, ((void *)&client_msg) + 1, sizeof(client_msg) - 1);
+ rfb_client_t *c = arg;
+ rfb_server_t *s = c->rc_s;
+ char tname[MAXCOMLEN + 1];
+ uint_t counter = 0;
+ hrtime_t tprev;
+ void *status;
+ int err;
- if (client_msg.subtype == RFB_CLIENTMSG_EXT_KEYEVENT ) {
- (void)stream_read(cfd, ((void *)&extkey_msg) + 2, sizeof(extkey_msg) - 2);
- console_key_event((int)extkey_msg.down, htonl(extkey_msg.sym), htonl(extkey_msg.code));
- rc->input_detected = true;
- }
-}
+ (void) snprintf(tname, sizeof (tname), "rfb%u tx", c->rc_instance);
+ (void) pthread_set_name_np(c->rc_tx_tid, tname);
-static void
-rfb_recv_ptr_msg(struct rfb_softc *rc, int cfd)
-{
- struct rfb_ptr_msg ptr_msg;
+ c->rc_sinfo.rsi_pixfmt = c->rc_s->rs_pixfmt;
+ c->rc_encodings = RFB_ENCODING_RAW;
- (void)stream_read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1);
+ if (!rfb_handshake(c))
+ goto out;
- console_ptr_event(ptr_msg.button, htons(ptr_msg.x), htons(ptr_msg.y));
- rc->input_detected = true;
-}
+ c->rc_cells = howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, RFB_PIX_PER_CELL);
+ if ((c->rc_crc = calloc(c->rc_cells, sizeof (uint32_t))) == NULL ||
+ (c->rc_crc_tmp = calloc(c->rc_cells, sizeof (uint32_t))) == NULL) {
+ perror("calloc crc");
+ goto out;
+ }
-static void
-rfb_recv_cuttext_msg(struct rfb_softc *rc, int cfd)
-{
- struct rfb_cuttext_msg ct_msg;
- unsigned char buf[32];
- int len;
-
- len = stream_read(cfd, ((void *)&ct_msg) + 1, sizeof(ct_msg) - 1);
- ct_msg.length = htonl(ct_msg.length);
- while (ct_msg.length > 0) {
- len = stream_read(cfd, buf, ct_msg.length > sizeof(buf) ?
- sizeof(buf) : ct_msg.length);
- ct_msg.length -= len;
+ err = pthread_create(&c->rc_rx_tid, NULL, rfb_client_rx_thread, c);
+ if (err != 0) {
+ perror("pthread_create client rx thread");
+ goto out;
}
-}
-static int64_t
-timeval_delta(struct timeval *prev, struct timeval *now)
-{
- int64_t n1, n2;
- n1 = now->tv_sec * 1000000 + now->tv_usec;
- n2 = prev->tv_sec * 1000000 + prev->tv_usec;
- return (n1 - n2);
-}
+ (void) snprintf(tname, sizeof (tname), "rfb%u rx", c->rc_instance);
+ (void) pthread_set_name_np(c->rc_rx_tid, tname);
-static void *
-rfb_wr_thr(void *arg)
-{
- struct rfb_softc *rc;
- fd_set rfds;
- struct timeval tv;
- struct timeval prev_tv;
- int64_t tdiff;
- int cfd;
- int err;
+ tprev = gethrtime();
- rc = arg;
- cfd = rc->cfd;
+ while (!c->rc_closing) {
+ struct timeval tv;
+ hrtime_t tnow;
+ int64_t tdiff;
+ fd_set rfds;
+ int err;
- prev_tv.tv_sec = 0;
- prev_tv.tv_usec = 0;
- while (rc->cfd >= 0) {
FD_ZERO(&rfds);
- FD_SET(cfd, &rfds);
+ FD_SET(c->rc_fd, &rfds);
tv.tv_sec = 0;
- tv.tv_usec = CFD_SEL_DELAY;
+ tv.tv_usec = RFB_SEL_DELAY_US;
- err = select(cfd+1, &rfds, NULL, NULL, &tv);
+ err = select(c->rc_fd + 1, &rfds, NULL, NULL, &tv);
if (err < 0)
- return (NULL);
+ break;
- /* Determine if its time to push screen; ~24hz */
- gettimeofday(&tv, NULL);
- tdiff = timeval_delta(&prev_tv, &tv);
- if (tdiff >= SCREEN_POLL_DELAY) {
+ /* Determine if its time to push the screen; ~24hz. */
+ tnow = gethrtime();
+ tdiff = NSEC2USEC(tnow - tprev);
+ if (tdiff >= RFB_SCREEN_POLL_DELAY) {
bool input;
- prev_tv.tv_sec = tv.tv_sec;
- prev_tv.tv_usec = tv.tv_usec;
- input = atomic_exchange(&rc->input_detected, false);
+
+ tprev = tnow;
+
+ input = atomic_exchange(&c->rc_input_detected, false);
/*
- * Refresh the screen on every second trip through the loop,
- * or if keyboard/mouse input has been detected.
+ * Refresh the screen on every second trip through the
+ * loop, or if keyboard/mouse input has been detected.
*/
- if ((++rc->wrcount & 1) || input) {
- if (rfb_send_screen(rc, cfd) <= 0) {
- return (NULL);
- }
+ if ((++counter & 1) != 0 || input) {
+ if (!rfb_send_screen(c))
+ break;
}
} else {
- /* sleep */
- usleep(SCREEN_POLL_DELAY - tdiff);
+ (void) usleep(RFB_SCREEN_POLL_DELAY - tdiff);
}
}
+ rfb_printf(c, RFB_LOGWARN, "disconnected");
+
+out:
+
+ (void) pthread_join(c->rc_rx_tid, &status);
+ pthread_mutex_lock(&s->rs_clientlock);
+ s->rs_clientcount--;
+ list_remove(&s->rs_clients, c);
+ if (s->rs_exclusive && s->rs_clientcount == 0)
+ s->rs_exclusive = false;
+ id_free(rfb_idspace, c->rc_instance);
+ pthread_mutex_unlock(&s->rs_clientlock);
+
+ rfb_free_client(c);
return (NULL);
}
-void
-rfb_handle(struct rfb_softc *rc, int cfd)
+static void
+rfb_accept(int sfd, enum ev_type event, void *arg)
{
- const char *vbuf = "RFB 003.008\n";
- unsigned char buf[80];
- unsigned char *message = NULL;
-
-#ifndef NO_OPENSSL
- unsigned char challenge[AUTH_LENGTH];
- unsigned char keystr[PASSWD_LENGTH];
- unsigned char crypt_expected[AUTH_LENGTH];
-
- DES_key_schedule ks;
- int i;
-#endif
- uint8_t client_ver;
- uint8_t auth_type;
- pthread_t tid;
- uint32_t sres = 0;
- int len;
- int perror = 1;
-
- rc->cfd = cfd;
-
- /* 1a. Send server version */
- stream_write(cfd, vbuf, strlen(vbuf));
-
- /* 1b. Read client version */
- len = stream_read(cfd, buf, VERSION_LENGTH);
-#ifdef __FreeBSD__
- if (len == VERSION_LENGTH && !strncmp(vbuf, buf, VERSION_LENGTH - 2)) {
- client_ver = buf[VERSION_LENGTH - 2];
- }
-#else
- /* Work around gcc7 maybe-uninitialized warning */
- client_ver = CVERS_3_3;
- if (len == VERSION_LENGTH && !strncmp(vbuf, (char *)buf,
- VERSION_LENGTH - 2)) {
- client_ver = buf[VERSION_LENGTH - 2];
- }
-#endif
- if (client_ver != CVERS_3_8 && client_ver != CVERS_3_7) {
- /* only recognize 3.3, 3.7 & 3.8. Others dflt to 3.3 */
- client_ver = CVERS_3_3;
+ rfb_server_t *s = arg;
+ rfb_client_t *c = NULL;
+ struct sockaddr_storage cliaddr;
+ socklen_t len;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ int cfd, err;
+ uint_t cc;
+
+ rfb_printf(c, RFB_LOGDEBUG, "incoming connection");
+
+ len = sizeof (cliaddr);
+ cfd = accept(sfd, (struct sockaddr *)&cliaddr, &len);
+ if (cfd == -1) {
+ perror("client accept");
+ return;
}
- /* 2a. Send security type */
- buf[0] = 1;
-
- /* In versions 3.7 & 3.8, it's 2-way handshake */
- /* For version 3.3, server says what the authentication type must be */
-#ifndef NO_OPENSSL
- if (rc->password) {
- auth_type = SECURITY_TYPE_VNC_AUTH;
+ *host = *port = '\0';
+ if (cliaddr.ss_family == AF_UNIX) {
+ rfb_printf(NULL, RFB_LOGDEBUG, "connection on UNIX socket");
+ (void) strlcpy(host, "<UNIX>", sizeof (host));
} else {
- auth_type = SECURITY_TYPE_NONE;
- }
-#else
- auth_type = SECURITY_TYPE_NONE;
-#endif
-
- switch (client_ver) {
- case CVERS_3_7:
- case CVERS_3_8:
- buf[0] = 1;
- buf[1] = auth_type;
- stream_write(cfd, buf, 2);
-
- /* 2b. Read agreed security type */
- len = stream_read(cfd, buf, 1);
- if (buf[0] != auth_type) {
- /* deny */
- sres = htonl(1);
-#ifdef __FreeBSD__
- message = "Auth failed: authentication type mismatch";
-#else
- message = (unsigned char *)
- "Auth failed: authentication type mismatch";
-#endif
- goto report_and_done;
- }
- break;
- case CVERS_3_3:
- default:
- be32enc(buf, auth_type);
- stream_write(cfd, buf, 4);
- break;
- }
-
- /* 2c. Do VNC authentication */
- switch (auth_type) {
- case SECURITY_TYPE_NONE:
- break;
- case SECURITY_TYPE_VNC_AUTH:
- /*
- * The client encrypts the challenge with DES, using a password
- * supplied by the user as the key.
- * To form the key, the password is truncated to
- * eight characters, or padded with null bytes on the right.
- * The client then sends the resulting 16-bytes response.
- */
-#ifndef NO_OPENSSL
-#ifdef __FreeBSD__
- strncpy(keystr, rc->password, PASSWD_LENGTH);
-#else
- strncpy((char *)keystr, rc->password, PASSWD_LENGTH);
-#endif
-
- /* VNC clients encrypts the challenge with all the bit fields
- * in each byte of the password mirrored.
- * Here we flip each byte of the keystr.
- */
- for (i = 0; i < PASSWD_LENGTH; i++) {
- keystr[i] = (keystr[i] & 0xF0) >> 4
- | (keystr[i] & 0x0F) << 4;
- keystr[i] = (keystr[i] & 0xCC) >> 2
- | (keystr[i] & 0x33) << 2;
- keystr[i] = (keystr[i] & 0xAA) >> 1
- | (keystr[i] & 0x55) << 1;
- }
-
- /* Initialize a 16-byte random challenge */
- arc4random_buf(challenge, sizeof(challenge));
- stream_write(cfd, challenge, AUTH_LENGTH);
-
- /* Receive the 16-byte challenge response */
- stream_read(cfd, buf, AUTH_LENGTH);
-
- memcpy(crypt_expected, challenge, AUTH_LENGTH);
-
- /* Encrypt the Challenge with DES */
- DES_set_key((const_DES_cblock *)keystr, &ks);
- DES_ecb_encrypt((const_DES_cblock *)challenge,
- (const_DES_cblock *)crypt_expected,
- &ks, DES_ENCRYPT);
- DES_ecb_encrypt((const_DES_cblock *)(challenge + PASSWD_LENGTH),
- (const_DES_cblock *)(crypt_expected +
- PASSWD_LENGTH),
- &ks, DES_ENCRYPT);
-
- if (memcmp(crypt_expected, buf, AUTH_LENGTH) != 0) {
-#ifdef __FreeBSD__
- message = "Auth Failed: Invalid Password.";
-#else
- message =
- (unsigned char *)"Auth Failed: Invalid Password.";
-#endif
- sres = htonl(1);
+ err = getnameinfo((struct sockaddr *)&cliaddr, len,
+ host, sizeof (host), port, sizeof (port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (err != 0) {
+ rfb_printf(NULL, RFB_LOGERR, "getnameinfo: %s",
+ gai_strerror(err));
+ *host = *port = '\0';
} else {
- sres = 0;
- }
-#else
- sres = htonl(1);
- WPRINTF(("Auth not supported, no OpenSSL in your system"));
-#endif
-
- break;
- }
-
- switch (client_ver) {
- case CVERS_3_7:
- case CVERS_3_8:
-report_and_done:
- /* 2d. Write back a status */
- stream_write(cfd, &sres, 4);
-
- if (sres) {
- /* 3.7 does not want string explaining cause */
- if (client_ver == CVERS_3_8) {
-#ifdef __FreeBSD__
- be32enc(buf, strlen(message));
- stream_write(cfd, buf, 4);
- stream_write(cfd, message, strlen(message));
-#else
- be32enc(buf, strlen((char *)message));
- stream_write(cfd, buf, 4);
- stream_write(cfd, message,
- strlen((char *)message));
-#endif
- }
- goto done;
- }
- break;
- case CVERS_3_3:
- default:
- /* for VNC auth case send status */
- if (auth_type == SECURITY_TYPE_VNC_AUTH) {
- /* 2d. Write back a status */
- stream_write(cfd, &sres, 4);
- }
- if (sres) {
- goto done;
+ rfb_printf(NULL, RFB_LOGDEBUG, "connection from %s:%s",
+ host, port);
}
- break;
- }
- /* 3a. Read client shared-flag byte */
- len = stream_read(cfd, buf, 1);
-
- /* 4a. Write server-init info */
-#ifndef __FreeBSD__
- rfb_send_server_init_msg(cfd, rc);
-#else
- rfb_send_server_init_msg(cfd);
-#endif
-
- if (!rc->zbuf) {
- rc->zbuf = malloc(RFB_ZLIB_BUFSZ + 16);
- assert(rc->zbuf != NULL);
}
- perror = pthread_create(&tid, NULL, rfb_wr_thr, rc);
- if (perror == 0)
- pthread_set_name_np(tid, "rfbout");
-
- /* Now read in client requests. 1st byte identifies type */
- for (;;) {
- len = read(cfd, buf, 1);
- if (len <= 0) {
- DPRINTF(("rfb client exiting"));
- break;
- }
-
- switch (buf[0]) {
- case CS_SET_PIXEL_FORMAT:
- rfb_recv_set_pixfmt_msg(rc, cfd);
- break;
- case CS_SET_ENCODINGS:
- rfb_recv_set_encodings_msg(rc, cfd);
- break;
- case CS_UPDATE_MSG:
- rfb_recv_update_msg(rc, cfd);
- break;
- case CS_KEY_EVENT:
- rfb_recv_key_msg(rc, cfd);
- break;
- case CS_POINTER_EVENT:
- rfb_recv_ptr_msg(rc, cfd);
- break;
- case CS_CUT_TEXT:
- rfb_recv_cuttext_msg(rc, cfd);
- break;
- case CS_MSG_CLIENT_QEMU:
- rfb_recv_client_msg(rc, cfd);
- break;
- default:
- WPRINTF(("rfb unknown cli-code %d!", buf[0] & 0xff));
- goto done;
- }
+ pthread_mutex_lock(&s->rs_clientlock);
+ cc = s->rs_clientcount;
+ pthread_mutex_unlock(&s->rs_clientlock);
+ if (cc >= RFB_MAX_CLIENTS) {
+ rfb_printf(NULL, RFB_LOGERR,
+ "too many clients, closing connection.");
+ goto fail;
}
-done:
- rc->cfd = -1;
- if (perror == 0)
- pthread_join(tid, NULL);
- if (rc->enc_zlib_ok)
- deflateEnd(&rc->zstream);
-}
-static void *
-rfb_thr(void *arg)
-{
- struct rfb_softc *rc;
- sigset_t set;
-
- int cfd;
-
- rc = arg;
-
- sigemptyset(&set);
- sigaddset(&set, SIGPIPE);
- if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
- perror("pthread_sigmask");
- return (NULL);
+ if ((c = calloc(1, sizeof (rfb_client_t))) == NULL) {
+ perror("calloc client");
+ goto fail;
}
- for (;;) {
- rc->enc_raw_ok = false;
- rc->enc_zlib_ok = false;
- rc->enc_resize_ok = false;
- rc->enc_extkeyevent_ok = false;
+ c->rc_fd = cfd;
+ c->rc_s = s;
+ c->rc_zbuf = malloc(RFB_ZLIB_BUFSZ + 16);
+ if (c->rc_zbuf == NULL)
+ goto fail;
- rc->enc_extkeyevent_send = false;
+ pthread_mutex_lock(&s->rs_clientlock);
- cfd = accept(rc->sfd, NULL, NULL);
- if (rc->conn_wait) {
- pthread_mutex_lock(&rc->mtx);
- pthread_cond_signal(&rc->cond);
- pthread_mutex_unlock(&rc->mtx);
- rc->conn_wait = 0;
- }
- rfb_handle(rc, cfd);
- close(cfd);
+ err = pthread_create(&c->rc_tx_tid, NULL, rfb_client_tx_thread, c);
+ if (err != 0) {
+ perror("pthread_create client tx thread");
+ pthread_mutex_unlock(&s->rs_clientlock);
+ goto fail;
}
- /* NOTREACHED */
- return (NULL);
-}
+ s->rs_clientcount++;
+ list_insert_tail(&s->rs_clients, c);
+ c->rc_instance = id_allocff(rfb_idspace);
+ pthread_mutex_unlock(&s->rs_clientlock);
-static int
-sse42_supported(void)
-{
- u_int cpu_registers[4], ecx;
+ (void) pthread_detach(c->rc_tx_tid);
- do_cpuid(1, cpu_registers);
+ rfb_printf(c, RFB_LOGWARN, "connection from %s", host);
- ecx = cpu_registers[2];
+ return;
- return ((ecx & CPUID2_SSE42) != 0);
+fail:
+ (void) close(cfd);
+ free(c);
}
int
-#ifndef __FreeBSD__
-rfb_init(char *hostname, int port, int wait, char *password, const char *name)
-#else
-rfb_init(char *hostname, int port, int wait, char *password)
-#endif
+rfb_init(char *hostname, int port, int wait, const char *password,
+ const char *name)
{
- int e;
- char servname[6];
- struct rfb_softc *rc;
- struct addrinfo *ai = NULL;
- struct addrinfo hints;
- int on = 1;
- int cnt;
+ rfb_server_t *s;
#ifndef WITHOUT_CAPSICUM
cap_rights_t rights;
#endif
- rc = calloc(1, sizeof(struct rfb_softc));
-
- cnt = howmany(RFB_MAX_WIDTH, PIX_PER_CELL) *
- howmany(RFB_MAX_HEIGHT, PIX_PER_CELL);
- rc->crc = calloc(cnt, sizeof(uint32_t));
- rc->crc_tmp = calloc(cnt, sizeof(uint32_t));
- rc->crc_width = RFB_MAX_WIDTH;
- rc->crc_height = RFB_MAX_HEIGHT;
- rc->sfd = -1;
+ (void) pthread_once(&rfb_once, rfb_init_once);
- rc->password = password;
-
-#ifndef __FreeBSD__
- rc->name = name;
-#endif
-
- snprintf(servname, sizeof(servname), "%d", port ? port : 5900);
-
- if (!hostname || strlen(hostname) == 0)
-#if defined(INET)
- hostname = "127.0.0.1";
-#elif defined(INET6)
- hostname = "[::1]";
-#endif
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
-
- if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
- EPRINTLN("getaddrinfo: %s", gai_strerror(e));
- goto error;
+ if (rfb_idspace == NULL) {
+ rfb_printf(NULL, RFB_LOGERR,
+ "rfb_idspace could not be allocated");
+ return (-1);
}
- rc->sfd = socket(ai->ai_family, ai->ai_socktype, 0);
- if (rc->sfd < 0) {
- perror("socket");
- goto error;
+ if ((s = calloc(1, sizeof (rfb_server_t))) == NULL) {
+ perror("calloc");
+ return (-1);
}
+ s->rs_fd = -1;
+ s->rs_name = name;
- setsockopt(rc->sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- if (bind(rc->sfd, ai->ai_addr, ai->ai_addrlen) < 0) {
- perror("bind");
- goto error;
- }
+ if (password != NULL && strlen(password) > 0)
+ s->rs_password = password;
- if (listen(rc->sfd, 1) < 0) {
- perror("listen");
- goto error;
+ if (pthread_mutex_init(&s->rs_clientlock, NULL) != 0) {
+ perror("pthread_mutex_init");
+ free(s);
+ return (-1);
}
-#ifndef WITHOUT_CAPSICUM
- cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE);
- if (caph_rights_limit(rc->sfd, &rights) == -1)
- errx(EX_OSERR, "Unable to apply rights for sandbox");
-#endif
+ list_create(&s->rs_clients, sizeof (rfb_client_t),
+ offsetof(rfb_client_t, rc_node));
+
+ /* Server pixel format. */
+ s->rs_pixfmt.rp_bpp = RFB_PIX_BPP;
+ s->rs_pixfmt.rp_depth = RFB_PIX_DEPTH;
+ s->rs_pixfmt.rp_bigendian = 0;
+ s->rs_pixfmt.rp_truecolour = 1;
+ s->rs_pixfmt.rp_r_max = htons(RFB_PIX_RMAX);
+ s->rs_pixfmt.rp_g_max = htons(RFB_PIX_GMAX);
+ s->rs_pixfmt.rp_b_max = htons(RFB_PIX_BMAX);
+ s->rs_pixfmt.rp_r_shift = RFB_PIX_RSHIFT;
+ s->rs_pixfmt.rp_g_shift = RFB_PIX_GSHIFT;
+ s->rs_pixfmt.rp_b_shift = RFB_PIX_BSHIFT;
+
+ /* UNIX socket. */
+ if (port == -1 && hostname != NULL && *hostname == '/') {
+ struct sockaddr_un sock;
+
+ s->rs_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (s->rs_fd < 0) {
+ perror("socket");
+ goto fail;
+ }
- rc->hw_crc = sse42_supported();
+ sock.sun_family = AF_UNIX;
+ if (strlcpy(sock.sun_path, hostname, sizeof (sock.sun_path)) >=
+ sizeof (sock.sun_path)) {
+ rfb_printf(NULL, RFB_LOGERR,
+ "socket path '%s' too long\n", hostname);
+ goto fail;
+ }
- rc->conn_wait = wait;
- if (wait) {
- pthread_mutex_init(&rc->mtx, NULL);
- pthread_cond_init(&rc->cond, NULL);
- }
+ (void) unlink(hostname);
+ if (bind(s->rs_fd, (struct sockaddr *)&sock,
+ sizeof (sock)) < 0) {
+ perror("bind");
+ goto fail;
+ }
+ } else {
+ struct addrinfo hints, *ai = NULL;
+ char servname[6];
+ int e;
- pthread_create(&rc->tid, NULL, rfb_thr, rc);
- pthread_set_name_np(rc->tid, "rfb");
+ (void) snprintf(servname, sizeof (servname), "%d",
+ port ? port : RFB_DEFAULT_PORT);
- if (wait) {
- DPRINTF(("Waiting for rfb client..."));
- pthread_mutex_lock(&rc->mtx);
- pthread_cond_wait(&rc->cond, &rc->mtx);
- pthread_mutex_unlock(&rc->mtx);
- DPRINTF(("rfb client connected"));
- }
+ if (hostname == NULL || strlen(hostname) == 0) {
+#if defined(INET)
+ hostname = "127.0.0.1";
+#elif defined(INET6)
+ hostname = "[::1]";
+#endif
+ }
- freeaddrinfo(ai);
- return (0);
+ memset(&hints, '\0', sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
- error:
- if (ai != NULL)
- freeaddrinfo(ai);
- if (rc->sfd != -1)
- close(rc->sfd);
- free(rc->crc);
- free(rc->crc_tmp);
- free(rc);
- return (-1);
-}
+ if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
+ rfb_printf(NULL, RFB_LOGERR, "getaddrinfo: %s",
+ gai_strerror(e));
+ goto fail;
+ }
-#ifndef __FreeBSD__
-int
-rfb_init_unix(const char *path, int wait, char *password, const char *name)
-{
- struct rfb_softc *rc;
- struct sockaddr_un sock;
+ s->rs_fd = socket(ai->ai_family, ai->ai_socktype, 0);
+ if (s->rs_fd < 0) {
+ perror("socket");
+ freeaddrinfo(ai);
+ goto fail;
+ }
- if ((rc = calloc(1, sizeof (struct rfb_softc))) == NULL) {
- perror("calloc");
- return (-1);
- }
- rc->sfd = -1;
+ e = 1;
+ (void) setsockopt(s->rs_fd, SOL_SOCKET, SO_REUSEADDR,
+ &e, sizeof (e));
- if ((rc->crc = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
- sizeof (uint32_t))) == NULL) {
- perror("calloc");
- goto fail;
- }
- if ((rc->crc_tmp = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
- sizeof (uint32_t))) == NULL) {
- perror("calloc");
- goto fail;
+ if (bind(s->rs_fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+ perror("bind");
+ freeaddrinfo(ai);
+ goto fail;
+ }
+ freeaddrinfo(ai);
}
- rc->crc_width = RFB_MAX_WIDTH;
- rc->crc_height = RFB_MAX_HEIGHT;
- rc->password = password;
- rc->name = name;
-
- rc->sfd = socket(PF_UNIX, SOCK_STREAM, 0);
- if (rc->sfd < 0) {
- perror("socket");
+ if (listen(s->rs_fd, 5) < 0) {
+ perror("listen");
goto fail;
}
- sock.sun_family = AF_UNIX;
- if (strlcpy(sock.sun_path, path, sizeof (sock.sun_path)) >=
- sizeof (sock.sun_path)) {
- (void) fprintf(stderr, "socket path '%s' too long\n", path);
- goto fail;
- }
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE);
+ if (caph_rights_limit(s->rs_fd, &rights) == -1)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
- (void) unlink(path);
- if (bind(rc->sfd, (struct sockaddr *)&sock, sizeof (sock)) < 0) {
- perror("bind");
+ s->rs_connevent = mevent_add(s->rs_fd, EVF_READ, rfb_accept, s);
+ if (s->rs_connevent == NULL) {
+ rfb_printf(NULL, RFB_LOGERR,
+ "Failed to set up rfb connection mevent");
goto fail;
}
- if (listen(rc->sfd, 1) < 0) {
- perror("listen");
- goto fail;
- }
+ list_insert_tail(&rfb_list, s);
- rc->hw_crc = sse42_supported();
+ /*
+ * Wait for first connection. Since the mevent thread is
+ * not yet running, we can't rely on normal incoming connection
+ * handling.
+ */
+ if (wait != 0) {
+ fd_set rfds;
+ int e;
- rc->conn_wait = wait;
- if (wait) {
- VERIFY3S(pthread_mutex_init(&rc->mtx, NULL), ==, 0);
- VERIFY3S(pthread_cond_init(&rc->cond, NULL), ==, 0);
- }
+ rfb_printf(NULL, RFB_LOGWARN,
+ "holding boot until first client connection");
- VERIFY3S(pthread_create(&rc->tid, NULL, rfb_thr, rc), ==, 0);
- pthread_set_name_np(rc->tid, "rfb");
+ for (;;) {
+ FD_ZERO(&rfds);
+ FD_SET(s->rs_fd, &rfds);
- if (wait) {
- DPRINTF(("Waiting for rfb client...\n"));
- pthread_mutex_lock(&rc->mtx);
- VERIFY3S(pthread_cond_wait(&rc->cond, &rc->mtx), ==, 0);
- pthread_mutex_unlock(&rc->mtx);
+ e = select(s->rs_fd + 1, &rfds, NULL, NULL, NULL);
+ if (e < 0 && errno == EINTR)
+ continue;
+ if (e < 0 || FD_ISSET(s->rs_fd, &rfds))
+ break;
+ }
+ rfb_printf(NULL, RFB_LOGWARN, "continuing boot");
}
return (0);
fail:
- if (rc->sfd != -1) {
- VERIFY3S(close(rc->sfd), ==, 0);
- }
- free(rc->crc);
- free(rc->crc_tmp);
- free(rc);
+ if (s->rs_fd != -1)
+ VERIFY3S(close(s->rs_fd), ==, 0);
+ (void) pthread_mutex_destroy(&s->rs_clientlock);
+ list_destroy(&s->rs_clients);
+ free(s);
return (-1);
}
-#endif
diff --git a/usr/src/cmd/bhyve/rfb.h b/usr/src/cmd/bhyve/rfb.h
index f779b0a5df..b0667f5994 100644
--- a/usr/src/cmd/bhyve/rfb.h
+++ b/usr/src/cmd/bhyve/rfb.h
@@ -3,7 +3,7 @@
*
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright 2018 Joyent, Inc.
- * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,15 +33,9 @@
#ifndef _RFB_H_
#define _RFB_H_
-#define RFB_PORT 5900
+#define RFB_DEFAULT_PORT 5900
-#ifndef __FreeBSD__
-int rfb_init(char *hostname, int port, int wait, char *password,
+int rfb_init(char *hostname, int port, int wait, const char *password,
const char *name);
-int rfb_init_unix(const char *path, int wait, char *password,
- const char *name);
-#else
-int rfb_init(char *hostname, int port, int wait, char *password);
-#endif
#endif /* _RFB_H_ */
diff --git a/usr/src/cmd/bhyve/rfb_impl.h b/usr/src/cmd/bhyve/rfb_impl.h
new file mode 100644
index 0000000000..883c9d7a0d
--- /dev/null
+++ b/usr/src/cmd/bhyve/rfb_impl.h
@@ -0,0 +1,252 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#ifndef _RFB_IMPL_H
+#define _RFB_IMPL_H
+
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/list.h>
+
+#include "mevent.h"
+
+/*
+ * The ProtocolVersion message consists of 12 bytes interpreted as a string of
+ * ASCII characters in the format "RFB xxx.yyy\n" where xxx and yyy are the
+ * major and minor version numbers, padded with zeros.
+ */
+#define RFB_VERSION "RFB 003.008\n"
+#define RFB_VERSION_LEN (sizeof (RFB_VERSION) - 1)
+
+_Static_assert(RFB_VERSION_LEN == 12, "RFB_VERSION length incorrect");
+
+/* Keep synchronised with pci_fbuf.c */
+#define RFB_MAX_WIDTH 1920
+#define RFB_MAX_HEIGHT 1200
+
+#define RFB_MAX_CLIENTS 10
+
+/* Framebuffer pixel format */
+#define RFB_PIX_BPP 32
+#define RFB_PIX_DEPTH 24
+#define RFB_PIX_RSHIFT 16
+#define RFB_PIX_GSHIFT 8
+#define RFB_PIX_BSHIFT 0
+#define RFB_PIX_RMAX 255
+#define RFB_PIX_GMAX 255
+#define RFB_PIX_BMAX 255
+
+#define RFB_ZLIB_BUFSZ (RFB_MAX_WIDTH * RFB_MAX_HEIGHT * 4)
+
+#define RFB_PIX_PER_CELL 32
+#define RFB_PIXCELL_SHIFT 5
+#define RFB_PIXCELL_MASK 0x1f
+#define RFB_SENDALL_THRESH 25
+
+#define RFB_SEL_DELAY_US 10000
+#define RFB_SCREEN_REFRESH_DELAY 33300 /* 30 Hz */
+#define RFB_SCREEN_POLL_DELAY (RFB_SCREEN_REFRESH_DELAY / 2)
+
+/* Client-to-server message types */
+#define RFBP_CS_SET_PIXEL_FORMAT 0
+#define RFBP_CS_SET_ENCODINGS 2
+#define RFBP_CS_UPDATE_REQUEST 3
+#define RFBP_CS_KEY_EVENT 4
+#define RFBP_CS_POINTER_EVENT 5
+#define RFBP_CS_CUT_TEXT 6
+#define RFBP_CS_QEMU 255
+#define RFBP_CS_QEMU_KEVENT 0
+
+/* Server-to-client message types */
+#define RFBP_SC_UPDATE 0
+#define RFBP_SC_SET_COLOURMAP_ENTRIES 1
+#define RFBP_SC_BELL 2
+#define RFBP_SC_CUT_TEXT 3
+
+/* Encodings */
+#define RFBP_ENCODING_RAW 0
+#define RFBP_ENCODING_ZLIB 6
+/* Pseudo-encodings */
+#define RFBP_ENCODING_RESIZE -223
+#define RFBP_ENCODING_EXT_KEVENT -258 /* QEMU ext. key event */
+#define RFBP_ENCODING_DESKTOP_NAME -307
+
+/* Security types */
+#define RFBP_SECURITY_INVALID 0
+#define RFBP_SECURITY_NONE 1
+#define RFBP_SECURITY_VNC_AUTH 2
+
+#define RFBP_SECURITY_VNC_AUTH_LEN 16
+#define RFBP_SECURITY_VNC_PASSWD_LEN 8
+
+typedef enum rfb_loglevel {
+ RFB_LOGDEBUG,
+ RFB_LOGWARN,
+ RFB_LOGERR
+} rfb_loglevel_t;
+
+typedef enum rfb_encodings {
+ RFB_ENCODING_RAW = (1ULL << 0),
+ RFB_ENCODING_ZLIB = (1ULL << 1),
+ RFB_ENCODING_RESIZE = (1ULL << 2),
+ RFB_ENCODING_EXT_KEVENT = (1ULL << 3),
+ RFB_ENCODING_DESKTOP_NAME = (1ULL << 4)
+} rfb_encodings_t;
+
+typedef enum rfb_cver {
+ RFB_CVER_3_3,
+ RFB_CVER_3_7,
+ RFB_CVER_3_8
+} rfb_cver_t;
+
+typedef struct rfb_pixfmt {
+ uint8_t rp_bpp;
+ uint8_t rp_depth;
+ uint8_t rp_bigendian;
+ uint8_t rp_truecolour;
+ uint16_t rp_r_max;
+ uint16_t rp_g_max;
+ uint16_t rp_b_max;
+ uint8_t rp_r_shift;
+ uint8_t rp_g_shift;
+ uint8_t rp_b_shift;
+ uint8_t rp_pad[3];
+} __packed rfb_pixfmt_t;
+
+/* Server-to-client message formats */
+
+typedef struct rfb_server_info {
+ uint16_t rsi_width;
+ uint16_t rsi_height;
+ rfb_pixfmt_t rsi_pixfmt;
+ uint32_t rsi_namelen;
+} __packed rfb_server_info_t;
+
+typedef struct rfb_server_update_msg {
+ uint8_t rss_type;
+ uint8_t rss_pad;
+ uint16_t rss_numrects;
+} __packed rfb_server_update_msg_t;
+
+typedef struct rfb_rect_hdr {
+ uint16_t rr_x;
+ uint16_t rr_y;
+ uint16_t rr_width;
+ uint16_t rr_height;
+ uint32_t rr_encoding;
+} __packed rfb_rect_hdr_t;
+
+/* Client-to-server message formats */
+
+typedef struct rfb_cs_pixfmt_msg {
+ uint8_t rp_pad[3];
+ rfb_pixfmt_t rp_pixfmt;
+} __packed rfb_cs_pixfmt_msg_t;
+
+typedef struct rfb_cs_update_msg {
+ uint8_t rum_incremental;
+ uint16_t rum_x;
+ uint16_t rum_y;
+ uint16_t rum_width;
+ uint16_t rum_height;
+} __packed rfb_cs_update_msg_t;
+
+typedef struct rfb_cs_encodings_msg {
+ uint8_t re_pad;
+ uint16_t re_numencs;
+} __packed rfb_cs_encodings_msg_t;
+
+typedef struct rfb_cs_key_event_msg {
+ uint8_t rke_down;
+ uint16_t rke_pad;
+ uint32_t rke_sym;
+} __packed rfb_cs_key_event_msg_t;
+
+typedef struct rfb_cs_pointer_event_msg {
+ uint8_t rpe_button;
+ uint16_t rpe_x;
+ uint16_t rpe_y;
+} __packed rfb_cs_pointer_event_msg_t;
+
+typedef struct rfb_cs_cut_text_msg {
+ uint8_t rct_padding[3];
+ uint32_t rct_length;
+} __packed rfb_cs_cut_text_msg_t;
+
+typedef struct rfb_cs_qemu_msg {
+ uint8_t rq_subtype;
+} __packed rfb_cs_qemu_msg_t;
+
+typedef struct rfb_cs_qemu_extended_key_msg {
+ uint16_t rqek_down;
+ uint32_t rqek_sym;
+ uint32_t rqek_code;
+} __packed rfb_cs_qemu_extended_key_msg_t;
+
+/* Client/server data structures */
+
+typedef struct rfb_server {
+ list_node_t rs_node;
+ int rs_fd;
+ const char *rs_name;
+ const char *rs_password;
+
+ struct mevent *rs_connevent;
+
+ uint_t rs_clientcount;
+ pthread_mutex_t rs_clientlock;
+ list_t rs_clients;
+
+ bool rs_exclusive;
+
+ rfb_pixfmt_t rs_pixfmt;
+} rfb_server_t;
+
+typedef struct rfb_client {
+ list_node_t rc_node;
+ uint_t rc_instance;
+
+ int rc_fd;
+ rfb_server_t *rc_s;
+ rfb_server_info_t rc_sinfo;
+ pthread_t rc_rx_tid;
+ pthread_t rc_tx_tid;
+
+ int rc_width;
+ int rc_height;
+ size_t rc_cells;
+ uint32_t *rc_crc;
+ uint32_t *rc_crc_tmp;
+ z_stream rc_zstream;
+ uint8_t *rc_zbuf;
+
+ struct bhyvegc_image rc_gci;
+
+ rfb_cver_t rc_cver;
+ rfb_encodings_t rc_encodings;
+
+ atomic_bool rc_closing;
+ atomic_bool rc_pending;
+ atomic_bool rc_input_detected;
+ atomic_bool rc_crc_reset;
+ atomic_bool rc_send_fullscreen;
+
+ bool rc_custom_pixfmt;
+ bool rc_keyevent_sent;
+} rfb_client_t;
+
+#endif /* _RFB_IMPL_H */
diff --git a/usr/src/lib/lib9p/common/backend/fs.c b/usr/src/lib/lib9p/common/backend/fs.c
index 4b7764cd86..792e17ebd2 100644
--- a/usr/src/lib/lib9p/common/backend/fs.c
+++ b/usr/src/lib/lib9p/common/backend/fs.c
@@ -1838,8 +1838,11 @@ fs_remove(void *softc, struct l9p_fid *fid)
return (error);
if (unlinkat(file->ff_dirfd, file->ff_name,
- S_ISDIR(cst.st_mode) ? AT_REMOVEDIR : 0) != 0)
+ S_ISDIR(cst.st_mode) ? AT_REMOVEDIR : 0) != 0) {
error = errno;
+ if (error == EEXIST && S_ISDIR(cst.st_mode))
+ error = ENOTEMPTY;
+ }
return (error);
}
@@ -3038,8 +3041,11 @@ fs_unlinkat(void *softc, struct l9p_request *req)
return (error);
if (req->lr_req.tunlinkat.flags & L9PL_AT_REMOVEDIR) {
- if (unlinkat(dirff->ff_dirfd, newname, AT_REMOVEDIR) != 0)
+ if (unlinkat(dirff->ff_dirfd, newname, AT_REMOVEDIR) != 0) {
error = errno;
+ if (error == EEXIST)
+ error = ENOTEMPTY;
+ }
} else {
if (unlinkat(dirff->ff_dirfd, newname, 0) != 0)
error = errno;
diff --git a/usr/src/lib/lib9p/common/request.c b/usr/src/lib/lib9p/common/request.c
index 99885690af..644524e12d 100644
--- a/usr/src/lib/lib9p/common/request.c
+++ b/usr/src/lib/lib9p/common/request.c
@@ -1410,12 +1410,12 @@ l9p_dispatch_trenameat(struct l9p_request *req)
int error;
error = fid_lookup(conn, req->lr_req.hdr.fid, ENOENT,
- F_REQUIRE_DIR | F_FORBID_OPEN, &req->lr_fid);
+ F_REQUIRE_DIR, &req->lr_fid);
if (error)
return (error);
error = fid_lookup(conn, req->lr_req.trenameat.newdirfid, ENOENT,
- F_REQUIRE_DIR | F_FORBID_OPEN, &req->lr_fid2);
+ F_REQUIRE_DIR, &req->lr_fid2);
if (error)
return (error);
@@ -1434,7 +1434,7 @@ l9p_dispatch_tunlinkat(struct l9p_request *req)
int error;
error = fid_lookup(conn, req->lr_req.hdr.fid, ENOENT,
- F_REQUIRE_DIR | F_FORBID_OPEN, &req->lr_fid);
+ F_REQUIRE_DIR, &req->lr_fid);
if (error)
return (error);
diff --git a/usr/src/lib/libidspace/common/libidspace.h b/usr/src/lib/libidspace/common/libidspace.h
index bb8690f19c..f78a3eb1fd 100644
--- a/usr/src/lib/libidspace/common/libidspace.h
+++ b/usr/src/lib/libidspace/common/libidspace.h
@@ -11,6 +11,7 @@
/*
* Copyright (c) 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
*/
#ifndef _LIBIDSPACE_H
@@ -32,6 +33,7 @@ extern id_space_t *id_space_create(const char *, id_t, id_t);
extern void id_space_destroy(id_space_t *);
extern void id_space_extend(id_space_t *, id_t, id_t);
extern id_t id_alloc(id_space_t *);
+extern id_t id_allocff(id_space_t *);
extern id_t id_alloc_specific(id_space_t *, id_t);
extern void id_free(id_space_t *, id_t);
diff --git a/usr/src/lib/libidspace/common/mapfile-vers b/usr/src/lib/libidspace/common/mapfile-vers
index 61ae855ee0..248c75b2e3 100644
--- a/usr/src/lib/libidspace/common/mapfile-vers
+++ b/usr/src/lib/libidspace/common/mapfile-vers
@@ -11,6 +11,7 @@
#
# Copyright (c) 2014 Joyent, Inc. All rights reserved.
+# Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
#
#
@@ -31,7 +32,8 @@ $mapfile_version 2
SYMBOL_VERSION ILLUMOS_1.0 { # first release of libidspace
global:
- id_alloc;
+ id_alloc;
+ id_allocff;
id_alloc_specific;
id_free;
id_space_create;
diff --git a/usr/src/lib/libumem/common/umem_update_thread.c b/usr/src/lib/libumem/common/umem_update_thread.c
index eb044b7641..fc64f9320b 100644
--- a/usr/src/lib/libumem/common/umem_update_thread.c
+++ b/usr/src/lib/libumem/common/umem_update_thread.c
@@ -142,6 +142,7 @@ umem_create_update_thread(void)
THR_BOUND | THR_DAEMON | THR_DETACHED | THR_SUSPENDED,
&newthread) == 0) {
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
+ (void) thr_setname(newthread, "umem_update");
(void) mutex_lock(&umem_update_lock);
/*
diff --git a/usr/src/man/man5/bhyve_config.5 b/usr/src/man/man5/bhyve_config.5
index 5e9caa9857..17788deaf8 100644
--- a/usr/src/man/man5/bhyve_config.5
+++ b/usr/src/man/man5/bhyve_config.5
@@ -25,7 +25,7 @@
.\"
.\" Portions Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
.\"
-.Dd February 26, 2022
+.Dd April 6, 2022
.Dt BHYVE_CONFIG 5
.Os
.Sh NAME
@@ -159,6 +159,16 @@ No VM will be started.
Enable debug messages relating to privilege management.
These messages are sent to
.Dv stdout .
+.It Va rfb.debug Ta bool Ta false Ta
+Enable debug messages relating to the RFB
+.Pq VNC
+server.
+.It Va xhci.debug Ta bool Ta false Ta
+Enable debug messages relating to the emulated XHCI
+.Pq USB
+controller.
+These messages are sent to
+.Dv stderr .
.El
.Ss x86-Specific Settings
.Bl -column "x86.vmexit_on_pause" "integer" "Default"