From: Twaik Yont <9674930+twaik@users.noreply.github.com> Add a socket-based backend for android ioctl dispatch. This introduces a listener thread implementation based on a Unix seqpacket socket with epoll, along with a new android_ioctl_new() client-side path supporting regular replies and optional file descriptor passing via SCM_RIGHTS. The new backend is not wired up yet and is currently unused. Existing ioctl handling remains unchanged. This is a preparatory step for migrating ioctl dispatch to a socket-based transport. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/device.c | 224 ++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 4fba423af6f..02ccf2760cd 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -32,6 +32,9 @@ #include <stdarg.h> #include <sys/ioctl.h> #include <sys/socket.h> +#include <sys/un.h> +#include <sys/epoll.h> +#include <sys/uio.h> #include <unistd.h> #include "ntstatus.h" @@ -163,6 +166,14 @@ struct native_win_wrapper LONG ref; }; +#define IPC_SOCKET_NAME "\0\\Device\\WineAndroid" +#define IPC_SOCKET_ADDR_LEN ((socklen_t)(offsetof(struct sockaddr_un, sun_path) + sizeof(IPC_SOCKET_NAME) - 1)) + +static const struct sockaddr_un ipc_addr = { + .sun_family = AF_UNIX, + .sun_path = IPC_SOCKET_NAME, +}; + struct ioctl_header { int hwnd; @@ -1120,6 +1131,159 @@ NTSTATUS android_java_uninit( void *arg ) return STATUS_SUCCESS; } +static void *ioctl_thread_proc(void *arg) +{ + JavaVM *java_vm; + int listen_fd, epoll_fd; + + if (!(java_vm = *p_java_vm)) return NULL; /* not running under Java */ + init_java_thread( java_vm ); + create_desktop_view(); + + listen_fd = socket( AF_UNIX, SOCK_SEQPACKET, 0 ); + if (listen_fd < 0) { + _ERR( "Failed to open server socket: %s\n", strerror(errno) ); + return NULL; + } + + if (bind( listen_fd, (const struct sockaddr *)&ipc_addr, IPC_SOCKET_ADDR_LEN ) < 0 || + listen( listen_fd, 32 ) < 0) + { + _ERR( "Failed to bind server socket: %s\n", strerror(errno) ); + close( listen_fd ); + return NULL; + } + + epoll_fd = epoll_create1( EPOLL_CLOEXEC ); + if (epoll_fd < 0) + { + _ERR( "Failed to create server epoll: %s\n", strerror(errno) ); + close( listen_fd ); + return NULL; + } + + if (epoll_ctl( epoll_fd, EPOLL_CTL_ADD, listen_fd, + &(struct epoll_event){ .events = EPOLLIN, .data.fd = listen_fd } ) < 0) + { + close( epoll_fd ); + close( listen_fd ); + return NULL; + } + + for (;;) + { + struct epoll_event events[16]; + int i, count = epoll_wait( epoll_fd, events, ARRAY_SIZE(events), -1 ); + + if (count < 0) + { + if (errno == EINTR) continue; + break; + } + + for (i = 0; i < count; i++) + { + int fd = events[i].data.fd; + + if (fd == listen_fd) + { + int client = accept( listen_fd, NULL, NULL ); + if (client < 0) + { + if (errno == EINTR) continue; + continue; + } + + if (epoll_ctl( epoll_fd, EPOLL_CTL_ADD, client, + &(struct epoll_event){ .events = EPOLLIN, .data.fd = client } ) < 0) + close( client ); + continue; + } + + if (events[i].events & (EPOLLHUP | EPOLLERR)) + { + epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL ); + close( fd ); + continue; + } + + for (;;) + { + char buffer[1024], control[CMSG_SPACE(sizeof(int))]; + int code = 0, status = -EINVAL, reply_fd = -1; + ULONG_PTR reply_size = 0; + ssize_t ret; + struct iovec iov[2] = { { &code, sizeof(code) }, { buffer, sizeof(buffer) } }; + struct iovec reply_iov[2] = { { &status, sizeof(status) }, { buffer, 0 } }; + struct msghdr msg = { NULL, 0, iov, 2, NULL, 0, 0 }; + struct msghdr reply = { NULL, 0, reply_iov, 2, NULL, 0, 0 }; + struct cmsghdr *cmsg; + + ret = recvmsg( fd, &msg, MSG_DONTWAIT ); + if (ret < 0) + { + if (errno == EINTR) continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) break; + epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL ); + close( fd ); + break; + } + + if (!ret || ret < sizeof(code)) + { + epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL ); + close( fd ); + break; + } + + ret -= sizeof(code); + + if ((unsigned int)code < NB_IOCTLS) + { + if (ret >= sizeof(struct ioctl_header)) { + pthread_mutex_lock(&dispatch_ioctl_lock); + status = status_to_android_error (ioctl_funcs[code]( buffer, ret, sizeof(buffer), &reply_size, &reply_fd )); + pthread_mutex_unlock(&dispatch_ioctl_lock); + } + } + else + { + _FIXME( "ioctl %x not supported\n", code ); + status = -ENOTSUP; + } + + reply_iov[1].iov_len = reply_size; + if (reply_fd != -1) + { + reply.msg_control = control; + reply.msg_controllen = sizeof(control); + cmsg = CMSG_FIRSTHDR( &reply ); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(reply_fd)); + memcpy( CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd) ); + reply.msg_controllen = cmsg->cmsg_len; + } + + if (sendmsg( fd, &reply, 0 ) < 0) + { + if (reply_fd != -1) close( reply_fd ); + epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL ); + close( fd ); + break; + } + + if (reply_fd != -1) close( reply_fd ); + } + } + } + + close( epoll_fd ); + close( listen_fd ); + (*java_vm)->DetachCurrentThread( java_vm ); + return NULL; +} + void start_android_device(void) { void *ret_ptr; @@ -1169,6 +1333,66 @@ static int android_ioctl( enum android_ioctl code, void *in, DWORD in_size, void return status_to_android_error( status ); } +static int android_ioctl_new( enum android_ioctl code, void *in, DWORD in_size, void *out, DWORD *out_size, int *recv_fd ) +{ + static int device_fd = -1; + static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER; + int status, err = -ENOENT; + ssize_t ret; + char control[CMSG_SPACE(sizeof(int))]; + struct iovec iov[2] = { { &status, sizeof(status) }, { out, out_size ? *out_size : 0 } }; + struct msghdr msg = { NULL, 0, iov, (out && out_size) ? 2 : 1, + recv_fd ? control : NULL, recv_fd ? sizeof(control) : 0, 0 }; + struct cmsghdr *cmsg; + + pthread_mutex_lock( &device_mutex ); + + if (recv_fd) *recv_fd = -1; + + if (device_fd == -1) + { + device_fd = socket( AF_UNIX, SOCK_SEQPACKET, 0 ); + if (device_fd < 0) goto done; + if (connect( device_fd, (const struct sockaddr *)&ipc_addr, IPC_SOCKET_ADDR_LEN ) < 0) + { + close( device_fd ); + device_fd = -1; + goto done; + } + } + + ret = writev( device_fd, (struct iovec[]){ { &code, sizeof(code) }, { in, in_size } }, 2 ); + if (ret <= 0 || ret != sizeof(code) + in_size) goto disconnected; + + ret = recvmsg( device_fd, &msg, 0 ); + if (ret <= 0 || ret < sizeof(status)) goto disconnected; + + if (out && out_size) *out_size = ret - sizeof(status); + err = status; + + if (recv_fd) + for (cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg )) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && + cmsg->cmsg_len >= CMSG_LEN(sizeof(int))) + { + memcpy( recv_fd, CMSG_DATA(cmsg), sizeof(int) ); + break; + } + + goto done; + +disconnected: + close( device_fd ); + device_fd = -1; + WARN( "parent process is gone\n" ); + NtTerminateProcess( 0, 1 ); + err = -ENOENT; + +done: + pthread_mutex_unlock( &device_mutex ); + return err; +} + static void win_incRef( struct android_native_base_t *base ) { struct native_win_wrapper *win = (struct native_win_wrapper *)base; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10569