diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8 --- a/usr.sbin/bhyve/bhyve.8 +++ b/usr.sbin/bhyve/bhyve.8 @@ -891,7 +891,7 @@ .Bl -bullet .Sm off .It -.Op Cm rfb= Ar ip-and-port +.Op Cm rfb= Ar address .Op Cm ,w= Ar width .Op Cm ,h= Ar height .Op Cm ,vga= Ar vgaconf @@ -902,9 +902,9 @@ .Pp Configuration options are defined as follows: .Bl -tag -width 10n -.It Cm rfb= Ns Ar ip-and-port Pq or Cm tcp= Ns Ar ip-and-port -An IP address and a port VNC should listen on. -There are two formats: +.It Cm rfb= Ns Ar address Pq or Cm tcp= Ns Ar address +A UNIX domain socket or IP address and a port VNC should listen on. +There are three possible formats: .Pp .Bl -bullet -compact .It @@ -916,6 +916,10 @@ .Sm off .Cm \&[ Ar IPv6%zone Cm \&] Cm \&: Ar port .Sm on +.It +.Sm off +.Cm unix: Ar my/unix.sock +.Sm on .El .Pp The default is to listen on localhost IPv4 address and default VNC port 5900. diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5 --- a/usr.sbin/bhyve/bhyve_config.5 +++ b/usr.sbin/bhyve/bhyve_config.5 @@ -523,7 +523,7 @@ If specified, it must be a unicast MAC address. .El .Ss Frame Buffer Settings -.Bl -column "password" "[IP:]port" "127.0.0.1:5900" +.Bl -column "password" "unix:my/unix.sock" "127.0.0.1:5900" .It Sy Name Ta Sy Format Ta Sy Default Ta Sy Description .It Va wait Ta bool Ta false Ta Wait for a remote connection before starting the VM. @@ -535,6 +535,11 @@ .Xr getaddrinfo 3 . A bare port number may be given in which case the IPv4 localhost address is used. +.It Va rfb Ta +.Sm off +.Cm unix: Ar my/unix.sock Ta Ta +.Sm on +Alternatively, provide a path to a UNIX domain socket. .It Va vga Ta string Ta io Ta VGA configuration. More details are provided in diff --git a/usr.sbin/bhyve/pci_fbuf.c b/usr.sbin/bhyve/pci_fbuf.c --- a/usr.sbin/bhyve/pci_fbuf.c +++ b/usr.sbin/bhyve/pci_fbuf.c @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include @@ -94,6 +96,7 @@ } __packed memregs; /* rfb server */ + sa_family_t rfb_family; char *rfb_host; char *rfb_password; int rfb_port; @@ -252,11 +255,13 @@ value = get_config_value_node(nvl, "tcp"); if (value != NULL) { /* + * UNIX -- unix:path/to/socket.sock * IPv4 -- host-ip:port * IPv6 -- [host-ip%zone]:port * XXX for now port is mandatory for IPv4. */ if (value[0] == '[') { + sc->rfb_family = AF_INET6; cp = strchr(value + 1, ']'); if (cp == NULL || cp == value + 1) { EPRINTLN("fbuf: Invalid IPv6 address: \"%s\"", @@ -279,7 +284,21 @@ value); return (-1); } + } else if (strncmp("unix:", value, 5) == 0) { + if (strlen(value + 5) > SUNPATHLEN) { + EPRINTLN( + "fbuf: UNIX socket path too long: \"%s\"", + value + 5); + return (-1); + } else if (*(value + 5) == '\0') { + EPRINTLN("fbuf: UNIX socket path is empty"); + return (-1); + } else { + sc->rfb_family = AF_UNIX; + sc->rfb_host = strdup(value + 5); + } } else { + sc->rfb_family = AF_UNSPEC; cp = strchr(value, ':'); if (cp == NULL) { sc->rfb_port = atoi(value); @@ -433,7 +452,8 @@ memset((void *)sc->fb_base, 0, FB_SIZE); - error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait, sc->rfb_password); + error = rfb_init(sc->rfb_family, sc->rfb_host, sc->rfb_port, + sc->rfb_wait, sc->rfb_password); done: if (error) free(sc); diff --git a/usr.sbin/bhyve/rfb.h b/usr.sbin/bhyve/rfb.h --- a/usr.sbin/bhyve/rfb.h +++ b/usr.sbin/bhyve/rfb.h @@ -29,9 +29,11 @@ #ifndef _RFB_H_ #define _RFB_H_ +#include + #define RFB_PORT 5900 -int rfb_init(const char *hostname, int port, int wait, +int rfb_init(sa_family_t family, const char *hostname, int port, int wait, const char *password); #endif /* _RFB_H_ */ diff --git a/usr.sbin/bhyve/rfb.c b/usr.sbin/bhyve/rfb.c --- a/usr.sbin/bhyve/rfb.c +++ b/usr.sbin/bhyve/rfb.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -1254,13 +1255,15 @@ } int -rfb_init(const char *hostname, int port, int wait, const char *password) +rfb_init(sa_family_t family, const char *hostname, int port, int wait, + const char *password) { int e; char servname[6]; struct rfb_softc *rc; struct addrinfo *ai = NULL; struct addrinfo hints; + struct sockaddr_un sun; int on = 1; int cnt; #ifndef WITHOUT_CAPSICUM @@ -1301,25 +1304,42 @@ 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 (family == AF_UNIX) { + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + if (strlcpy(sun.sun_path, hostname, sizeof(sun.sun_path)) >= + sizeof(sun.sun_path)) { + EPRINTLN("rfb: socket path too long"); + goto error; + } + rc->sfd = socket(AF_UNIX, SOCK_STREAM, 0); + } else { + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = family; + 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; + } + rc->sfd = socket(ai->ai_family, ai->ai_socktype, 0); } - rc->sfd = socket(ai->ai_family, ai->ai_socktype, 0); if (rc->sfd < 0) { perror("socket"); goto error; } + /* No effect for UNIX domain sockets. */ setsockopt(rc->sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (bind(rc->sfd, ai->ai_addr, ai->ai_addrlen) < 0) { + if (family == AF_UNIX) { + unlink(hostname); + e = bind(rc->sfd, (struct sockaddr *)&sun, SUN_LEN(&sun)); + } else + e = bind(rc->sfd, ai->ai_addr, ai->ai_addrlen); + if (e < 0) { perror("bind"); goto error; } @@ -1355,14 +1375,17 @@ DPRINTF(("rfb client connected")); } - freeaddrinfo(ai); + if (family != AF_UNIX) + freeaddrinfo(ai); return (0); error: if (rc->pixfmt_mtx) pthread_mutex_destroy(&rc->pixfmt_mtx); - if (ai != NULL) + if (ai != NULL) { + assert(family != AF_UNIX); freeaddrinfo(ai); + } if (rc->sfd != -1) close(rc->sfd); free(rc->crc);