diff --git a/qemu-display/Cargo.toml b/qemu-display/Cargo.toml index d098325..b54c79b 100644 --- a/qemu-display/Cargo.toml +++ b/qemu-display/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cfg-if = "1.0" log = "0.4" derivative = "2.2.0" zbus = { version = "2.0", features = ["xml"] } @@ -23,3 +24,7 @@ usbredirhost = "0.0.1" async-broadcast = "0.3.3" async-trait = "0.1.48" async-lock = "2.3.0" + +[target.'cfg(windows)'.dependencies] +uds_windows = "1.0.1" +windows = { version = "0.39.0", features = ["Win32_Networking_WinSock", "Win32_Foundation", "Win32_System_IO", "Win32_System_Threading"] } diff --git a/qemu-display/src/audio.rs b/qemu-display/src/audio.rs index 7de5ae7..da82bad 100644 --- a/qemu-display/src/audio.rs +++ b/qemu-display/src/audio.rs @@ -1,6 +1,14 @@ -use std::os::unix::{io::AsRawFd, net::UnixStream}; -use zbus::{dbus_interface, dbus_proxy, zvariant::Fd, Connection}; +#[cfg(windows)] +use crate::win32::Fd; +#[cfg(unix)] +use std::os::unix::net::UnixStream; +#[cfg(windows)] +use uds_windows::UnixStream; +#[cfg(unix)] +use zbus::zvariant::Fd; +use zbus::{dbus_interface, dbus_proxy, Connection}; +use crate::util; use crate::Result; #[derive(Debug)] @@ -236,9 +244,8 @@ impl Audio { pub async fn register_out_listener(&mut self, handler: H) -> Result<()> { let (p0, p1) = UnixStream::pair()?; - self.proxy - .register_out_listener(p0.as_raw_fd().into()) - .await?; + let p0 = util::prepare_uds_pass(&p0)?; + self.proxy.register_out_listener(p0).await?; let c = zbus::ConnectionBuilder::unix_stream(p1) .p2p() .serve_at( @@ -253,9 +260,8 @@ impl Audio { pub async fn register_in_listener(&mut self, handler: H) -> Result<()> { let (p0, p1) = UnixStream::pair()?; - self.proxy - .register_in_listener(p0.as_raw_fd().into()) - .await?; + let p0 = util::prepare_uds_pass(&p0)?; + self.proxy.register_in_listener(p0).await?; let c = zbus::ConnectionBuilder::unix_stream(p1) .p2p() .serve_at( diff --git a/qemu-display/src/chardev.rs b/qemu-display/src/chardev.rs index 596f7c2..c68ab91 100644 --- a/qemu-display/src/chardev.rs +++ b/qemu-display/src/chardev.rs @@ -1,14 +1,16 @@ +#[cfg(windows)] +use crate::win32::Fd; use std::convert::TryFrom; -use zbus::{ - dbus_proxy, - zvariant::{Fd, ObjectPath}, -}; +#[cfg(unix)] +use zbus::zvariant::Fd; +use zbus::{dbus_proxy, zvariant::ObjectPath}; use crate::Result; #[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Chardev")] pub trait Chardev { /// Register method + #[cfg(unix)] fn register(&self, stream: Fd) -> zbus::Result<()>; /// SendBreak method diff --git a/qemu-display/src/console.rs b/qemu-display/src/console.rs index 68e15a7..c85d660 100644 --- a/qemu-display/src/console.rs +++ b/qemu-display/src/console.rs @@ -1,15 +1,15 @@ -use std::{ - cell::RefCell, - convert::TryFrom, - os::unix::{io::AsRawFd, net::UnixStream}, -}; -use zbus::{ - dbus_proxy, - zvariant::{Fd, ObjectPath}, - Connection, -}; +#[cfg(windows)] +use crate::win32::Fd; +#[cfg(unix)] +use std::os::unix::net::UnixStream; +use std::{cell::RefCell, convert::TryFrom}; +#[cfg(windows)] +use uds_windows::UnixStream; +#[cfg(unix)] +use zbus::zvariant::Fd; +use zbus::{dbus_proxy, zvariant::ObjectPath, Connection}; -use crate::{ConsoleListener, ConsoleListenerHandler, KeyboardProxy, MouseProxy, Result}; +use crate::{util, ConsoleListener, ConsoleListenerHandler, KeyboardProxy, MouseProxy, Result}; #[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Console")] pub trait Console { @@ -87,7 +87,8 @@ impl Console { pub async fn register_listener(&self, handler: H) -> Result<()> { let (p0, p1) = UnixStream::pair()?; - self.proxy.register_listener(p0.as_raw_fd().into()).await?; + let p0 = util::prepare_uds_pass(&p0)?; + self.proxy.register_listener(p0).await?; let c = zbus::ConnectionBuilder::unix_stream(p1) .p2p() .serve_at("/org/qemu/Display1/Listener", ConsoleListener::new(handler))? diff --git a/qemu-display/src/console_listener.rs b/qemu-display/src/console_listener.rs index 9b389c6..7648a63 100644 --- a/qemu-display/src/console_listener.rs +++ b/qemu-display/src/console_listener.rs @@ -1,9 +1,12 @@ +#[cfg(windows)] +use crate::win32::Fd; use derivative::Derivative; -use std::{ - ops::Drop, - os::unix::io::{AsRawFd, IntoRawFd, RawFd}, -}; -use zbus::{dbus_interface, zvariant::Fd}; +use std::ops::Drop; +#[cfg(unix)] +use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +use zbus::dbus_interface; +#[cfg(unix)] +use zbus::zvariant::Fd; #[derive(Derivative)] #[derivative(Debug)] @@ -29,6 +32,7 @@ pub struct Update { pub data: Vec, } +#[cfg(unix)] #[derive(Debug)] pub struct ScanoutDMABUF { pub fd: RawFd, @@ -40,6 +44,10 @@ pub struct ScanoutDMABUF { pub y0_top: bool, } +#[cfg(windows)] +#[derive(Debug)] +pub struct ScanoutDMABUF {} + #[derive(Derivative)] #[derivative(Debug)] pub struct Cursor { @@ -51,6 +59,7 @@ pub struct Cursor { pub data: Vec, } +#[cfg(unix)] impl Drop for ScanoutDMABUF { fn drop(&mut self) { if self.fd >= 0 { @@ -61,6 +70,7 @@ impl Drop for ScanoutDMABUF { } } +#[cfg(unix)] impl IntoRawFd for ScanoutDMABUF { fn into_raw_fd(mut self) -> RawFd { std::mem::replace(&mut self.fd, -1) @@ -148,6 +158,22 @@ impl ConsoleListener { .await; } + #[cfg(not(unix))] + #[dbus_interface(name = "ScanoutDMABUF")] + async fn scanout_dmabuf( + &mut self, + _fd: Fd, + _width: u32, + _height: u32, + _stride: u32, + _fourcc: u32, + _modifier: u64, + _y0_top: bool, + ) { + unimplemented!() + } + + #[cfg(unix)] #[dbus_interface(name = "ScanoutDMABUF")] async fn scanout_dmabuf( &mut self, diff --git a/qemu-display/src/display.rs b/qemu-display/src/display.rs index 42e6ef9..3682255 100644 --- a/qemu-display/src/display.rs +++ b/qemu-display/src/display.rs @@ -12,7 +12,9 @@ use zbus::{ }; use zvariant::OwnedObjectPath; -use crate::{Audio, Chardev, Clipboard, Error, Result, UsbRedir, VMProxy}; +#[cfg(unix)] +use crate::UsbRedir; +use crate::{Audio, Chardev, Clipboard, Error, Result, VMProxy}; struct Inner<'d> { proxy: fdo::ObjectManagerProxy<'d>, @@ -144,6 +146,7 @@ impl<'d> Display<'d> { .await } + #[cfg(unix)] pub async fn usbredir(&self) -> UsbRedir { let chardevs = stream::iter(self.chardevs().await) .filter_map(|c| async move { diff --git a/qemu-display/src/lib.rs b/qemu-display/src/lib.rs index 597ae2e..f33438b 100644 --- a/qemu-display/src/lib.rs +++ b/qemu-display/src/lib.rs @@ -1,5 +1,9 @@ #![allow(clippy::too_many_arguments)] +pub mod util; +#[cfg(windows)] +mod win32; + mod error; pub use error::*; @@ -30,7 +34,9 @@ pub use mouse::*; mod display; pub use display::*; +#[cfg(unix)] mod usbredir; +#[cfg(unix)] pub use usbredir::UsbRedir; #[cfg(test)] diff --git a/qemu-display/src/usbredir.rs b/qemu-display/src/usbredir.rs index a37c734..58ff2be 100644 --- a/qemu-display/src/usbredir.rs +++ b/qemu-display/src/usbredir.rs @@ -1,19 +1,22 @@ use async_broadcast::{broadcast, Receiver, Sender}; use async_lock::RwLock; use futures::Stream; +#[cfg(unix)] +use std::os::unix::{ + io::{AsRawFd, RawFd}, + net::UnixStream, +}; use std::{ collections::HashMap, default::Default, io::{Read, Write}, - os::unix::{ - io::{AsRawFd, RawFd}, - net::UnixStream, - }, pin::Pin, sync::{Arc, Mutex}, task::{Context, Poll}, thread::JoinHandle, }; +#[cfg(windows)] +use uds_windows::UnixStream; use usbredirhost::{ rusb::{self, UsbContext}, Device, DeviceHandler, LogLevel, @@ -24,6 +27,7 @@ use crate::{Chardev, Error, Result}; #[derive(Debug)] struct InnerHandler { #[allow(unused)] // keep the device opened, as rusb doesn't take it + #[cfg(unix)] device_fd: Option, stream: UnixStream, ctxt: rusb::Context, @@ -73,6 +77,7 @@ impl DeviceHandler for Handler { fn flush_writes(&mut self) {} } +#[cfg(unix)] #[zbus::dbus_proxy( interface = "org.freedesktop.usbredir1", default_service = "org.freedesktop.usbredir1", @@ -88,6 +93,7 @@ impl Handler { let (dev, device_fd) = match device.open() { Ok(it) => (it, None), + #[cfg(unix)] Err(rusb::Error::Access) => { let (bus, dev) = (device.bus_number(), device.address()); let sysbus = zbus::Connection::system().await?; @@ -120,6 +126,7 @@ impl Handler { let handler = Self { inner: Arc::new(Mutex::new(InnerHandler { + #[cfg(unix)] device_fd, stream, event, @@ -302,6 +309,7 @@ impl Stream for NFreeChannelsStream { } } +#[cfg(unix)] fn fd_poll_readable(fd: RawFd, wait: Option) -> std::io::Result { let mut fds = vec![libc::pollfd { fd, diff --git a/qemu-display/src/util.rs b/qemu-display/src/util.rs new file mode 100644 index 0000000..bbc7212 --- /dev/null +++ b/qemu-display/src/util.rs @@ -0,0 +1,45 @@ +use crate::Result; + +#[cfg(unix)] +use std::os::unix::{io::AsRawFd, net::UnixStream}; +#[cfg(unix)] +use zbus::zvariant::Fd; +#[cfg(windows)] +use win32::Fd; + +#[cfg(windows)] +use crate::win32; +#[cfg(windows)] +use std::os::windows::io::AsRawSocket; +#[cfg(windows)] +use uds_windows::UnixStream; +#[cfg(windows)] +use windows::Win32::Networking::WinSock::{WSADuplicateSocketW, SOCKET, WSAPROTOCOL_INFOW}; +#[cfg(windows)] +use windows::Win32::System::Threading::PROCESS_DUP_HANDLE; + +pub fn prepare_uds_pass(us: &UnixStream) -> Result { + #[cfg(unix)] + { + Ok(us.as_raw_fd().into()) + } + + #[cfg(windows)] + { + let pid = win32::unix_stream_get_peer_pid(us)?; + let p = win32::ProcessHandle::open(Some(pid), PROCESS_DUP_HANDLE)?; + let mut info = unsafe { std::mem::zeroed() }; + if unsafe { WSADuplicateSocketW(SOCKET(us.as_raw_socket() as _), p.process_id(), &mut info) } + != 0 + { + return Err(crate::Error::Io(win32::wsa_last_err())); + } + let info = unsafe { + std::slice::from_raw_parts( + &info as *const _ as *const u8, + std::mem::size_of::(), + ) + }; + Ok(info.to_vec()) + } +} diff --git a/qemu-display/src/win32.rs b/qemu-display/src/win32.rs new file mode 100644 index 0000000..013bb0e --- /dev/null +++ b/qemu-display/src/win32.rs @@ -0,0 +1,85 @@ +use std::io; +use uds_windows::UnixStream; +use windows::Win32::Foundation::{CloseHandle, HANDLE}; +use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS; + +pub type Fd = Vec; + +// A process handle +pub struct ProcessHandle(HANDLE); + +impl Drop for ProcessHandle { + fn drop(&mut self) { + unsafe { CloseHandle(self.0) }; + } +} + +impl ProcessHandle { + // Open the process associated with the process_id (if None, the current process) + pub fn open( + process_id: Option, + desired_access: PROCESS_ACCESS_RIGHTS, + ) -> Result { + use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcess}; + + let process = if let Some(process_id) = process_id { + unsafe { OpenProcess(desired_access, false, process_id)? } + } else { + unsafe { GetCurrentProcess() } + }; + + Ok(Self(process)) + } + + pub fn process_id(&self) -> u32 { + use windows::Win32::System::Threading::GetProcessId; + + unsafe { GetProcessId(self.0) } + } +} + +pub(crate) fn wsa_last_err() -> io::Error { + use windows::Win32::Networking::WinSock::WSAGetLastError; + + let err = unsafe { WSAGetLastError() }; + io::Error::from_raw_os_error(err.0) +} + +// Get the process ID of the connected peer +pub fn unix_stream_get_peer_pid(stream: &UnixStream) -> Result { + use std::os::windows::io::AsRawSocket; + use windows::Win32::Networking::WinSock::{ + WSAIoctl, IOC_OUT, IOC_VENDOR, SOCKET, SOCKET_ERROR, + }; + + macro_rules! _WSAIOR { + ($x:expr, $y:expr) => { + IOC_OUT | $x | $y + }; + } + + let socket = stream.as_raw_socket(); + const SIO_AF_UNIX_GETPEERPID: u32 = _WSAIOR!(IOC_VENDOR, 256); + let mut ret = 0 as u32; + let mut bytes = 0; + + let r = unsafe { + WSAIoctl( + SOCKET(socket as _), + SIO_AF_UNIX_GETPEERPID, + 0 as *mut _, + 0, + &mut ret as *mut _ as *mut _, + std::mem::size_of_val(&ret) as u32, + &mut bytes, + 0 as *mut _, + None, + ) + }; + + if r == SOCKET_ERROR { + return Err(wsa_last_err()); + } + + Ok(ret) +} diff --git a/qemu-rdw/Cargo.toml b/qemu-rdw/Cargo.toml index 16606e4..abaa95b 100644 --- a/qemu-rdw/Cargo.toml +++ b/qemu-rdw/Cargo.toml @@ -17,3 +17,4 @@ rdw = { package = "rdw4", version = "0.1", features = ["bindings"] } futures-util = "0.3" futures = "0.3" async-trait = "0.1" +uds_windows = "1.0.2" diff --git a/qemu-rdw/src/display.rs b/qemu-rdw/src/display.rs index c6a88f1..f7fa212 100644 --- a/qemu-rdw/src/display.rs +++ b/qemu-rdw/src/display.rs @@ -5,6 +5,7 @@ use keycodemap::KEYMAP_XORGEVDEV2QNUM; use once_cell::sync::OnceCell; use qemu_display::{Console, ConsoleListenerHandler}; use rdw::{gtk, DisplayExt}; +#[cfg(unix)] use std::os::unix::io::IntoRawFd; mod imp { @@ -158,6 +159,11 @@ mod imp { } widget.update_area(u.x as _, u.y as _, u.w as _, u.h as _, u.stride as _, &u.data); } + #[cfg(windows)] + ScanoutDMABUF(_) => { + unimplemented!() + } + #[cfg(unix)] ScanoutDMABUF(s) => { widget.set_display_size(Some((s.width as _, s.height as _))); widget.set_dmabuf_scanout(rdw::RdwDmabufScanout { diff --git a/qemu-rdw/src/main.rs b/qemu-rdw/src/main.rs index 80bd2fa..f3782e6 100644 --- a/qemu-rdw/src/main.rs +++ b/qemu-rdw/src/main.rs @@ -2,17 +2,19 @@ use futures_util::StreamExt; use gio::ApplicationFlags; use glib::MainContext; use gtk::{gio, glib, prelude::*}; -use qemu_display::{Chardev, Console, Display}; +use qemu_display::{util, Chardev, Console, Display}; use rdw::gtk; use std::{cell::RefCell, sync::Arc}; mod audio; mod clipboard; mod display; +#[cfg(unix)] mod usbredir; struct Inner { app: gtk::Application, + #[cfg(unix)] usbredir: RefCell>, audio: RefCell>, clipboard: RefCell>, @@ -101,6 +103,7 @@ impl App { let app = App { inner: Arc::new(Inner { app, + #[cfg(unix)] usbredir: Default::default(), audio: Default::default(), clipboard: Default::default(), @@ -176,6 +179,7 @@ impl App { .unwrap() .set_child(Some(&rdw)); + #[cfg(unix)] app_clone.set_usbredir(usbredir::Handler::new(display.usbredir().await)); if let Ok(Some(audio)) = display.audio().await { @@ -197,13 +201,15 @@ impl App { } if let Ok(c) = Chardev::new(&conn, "qmp").await { - use std::{ - io::{prelude::*, BufReader}, - os::unix::{io::AsRawFd, net::UnixStream}, - }; + use std::io::{prelude::*, BufReader}; + #[cfg(unix)] + use std::os::unix::net::UnixStream; + #[cfg(windows)] + use uds_windows::UnixStream; let (p0, p1) = UnixStream::pair().unwrap(); - if c.proxy.register(p1.as_raw_fd().into()).await.is_ok() { + let fd = util::prepare_uds_pass(&p1).unwrap(); + if c.proxy.register(fd).await.is_ok() { let mut reader = BufReader::new(p0.try_clone().unwrap()); let mut line = String::new(); std::thread::spawn(move || loop { @@ -218,22 +224,26 @@ impl App { }); }); - let action_usb = gio::SimpleAction::new("usb", None); - let app_clone = app.clone(); - action_usb.connect_activate(move |_, _| { - let usbredir = app_clone.inner.usbredir.borrow(); - if let Some(usbredir) = usbredir.as_ref() { - let dialog = gtk::Dialog::new(); - dialog.set_transient_for(app_clone.inner.app.active_window().as_ref()); - dialog.set_child(Some(&usbredir.widget())); - dialog.show(); - } - }); - app.inner.app.add_action(&action_usb); + #[cfg(unix)] + { + let action_usb = gio::SimpleAction::new("usb", None); + let app_clone = app.clone(); + action_usb.connect_activate(move |_, _| { + let usbredir = app_clone.inner.usbredir.borrow(); + if let Some(usbredir) = usbredir.as_ref() { + let dialog = gtk::Dialog::new(); + dialog.set_transient_for(app_clone.inner.app.active_window().as_ref()); + dialog.set_child(Some(&usbredir.widget())); + dialog.show(); + } + }); + app.inner.app.add_action(&action_usb); + } app } + #[cfg(unix)] fn set_usbredir(&self, usbredir: usbredir::Handler) { self.inner.usbredir.replace(Some(usbredir)); }