Revamp client's Windows POSIX compatibility code

Unlike POSIX sockets, Windows sockets are not file descriptors, but
"OS handles", with a completely separate set of functions.

However, Windows can create a file descriptor for a socket, and return
a file descriptor's underlying handle.  Use that instead of our gross
hacks to keep up the illusion that sockets are file descriptors.
Slightly dirty: we put file descriptors into fd_set.  Works because
both boil down to int.  Change w32_select(), w32_socket(),
w32_connect(), w32_recv(), w32_writev_socket(), w32_send() to take and
return only file descriptors, and map to sockets internally.  Replace
w32_close_socket() by w32_close(), and drop the close() macro hackery
that made tcp_connect(), host_connect() use w32_close_socket().  New
fd_is_socket().

Windows provides select()-like functions only for handles.  Because of
that, the client used a handle for reading script files, and stored it
in file descriptor input_fd.  Drop this dirty hack, use a file
descriptor instead.  Works because we can get its underlying handle.
Remove the dirty macro hackery that made play(), ring_from_file() and
doexecute() unwittingly work with a handle.  Remove w32_openhandle()
and w32_close_handle().  Replace w32_readv_handle() by w32_readv_fd().
Update w32_select().

Remove w32_openfd(), it's not really needed.

The old code stuffed WSA error codes into errno, which doesn't work.
Use new w32_set_winsock_errno() to convert & stuff.

Fix signed vs. unsigned warnings in Windows client.

Move the struct sigaction replacement next to the sigaction()
replacement.

Rename sysdep_init() to w32_sysdep_init() for consistency.
This commit is contained in:
Markus Armbruster 2009-04-13 15:08:13 +02:00
parent f4209f7ea9
commit 798af5b45b
8 changed files with 155 additions and 176 deletions

View file

@ -88,7 +88,7 @@ play.$O: linebuf.h misc.h sysdep_w32.h proto.h ringbuf.h secure.h
ringbuf.$O: misc.h sysdep_w32.h ringbuf.h ringbuf.$O: misc.h sysdep_w32.h ringbuf.h
secure.$O: ringbuf.h secure.h secure.$O: ringbuf.h secure.h
servcmd.$O: misc.h sysdep_w32.h proto.h secure.h servcmd.$O: misc.h sysdep_w32.h proto.h secure.h
sysdep_w32.$O: misc.h sysdep_w32.h linebuf.h ringbuf.h secure.h sysdep_w32.$O: misc.h sysdep_w32.h
termlib.$O: misc.h termlib.$O: misc.h
version.$O: version.h version.$O: version.h
$(obj): config.h $(obj): config.h

View file

@ -47,8 +47,6 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <unistd.h> #include <unistd.h>
#else
#define close(fd) w32_close_socket((fd))
#endif #endif
#include "misc.h" #include "misc.h"

View file

@ -31,7 +31,7 @@
* Dave Pare, 1986 * Dave Pare, 1986
* Steve McClure, 1998 * Steve McClure, 1998
* Ron Koenderink, 2004-2005 * Ron Koenderink, 2004-2005
* Markus Armbruster, 2005-2008 * Markus Armbruster, 2005-2009
*/ */
#include <config.h> #include <config.h>
@ -47,6 +47,7 @@
#ifdef _WIN32 #ifdef _WIN32
#define getuid() 0 #define getuid() 0
#define getpwuid(uid) ((void)(uid), w32_getpw()) #define getpwuid(uid) ((void)(uid), w32_getpw())
#define sysdep_init() w32_sysdep_init()
#else #else
#define sysdep_init() ((void)0) #define sysdep_init() ((void)0)
#endif #endif

View file

@ -28,7 +28,7 @@
* play.c: Playing the game * play.c: Playing the game
* *
* Known contributors to this file: * Known contributors to this file:
* Markus Armbruster, 2007 * Markus Armbruster, 2007-2009
* Ron Koenderink, 2007-2009 * Ron Koenderink, 2007-2009
*/ */
@ -71,6 +71,11 @@ static HANDLE bounce_full;
static HANDLE ctrl_c_event; static HANDLE ctrl_c_event;
static int bounce_status, bounce_error; static int bounce_status, bounce_error;
struct sigaction {
int sa_flags;
void (*sa_handler)(int sig);
};
#define SIGPIPE -1 #define SIGPIPE -1
static void (*ctrl_handler)(int sig) = { SIG_DFL }; static void (*ctrl_handler)(int sig) = { SIG_DFL };
@ -170,45 +175,45 @@ sysdep_stdin_init(void)
/* /*
* This function uses to WaitForMultipleObjects to wait for both * This function uses to WaitForMultipleObjects to wait for both
* stdin and socket reading or writing. * stdin and socket reading or writing.
* Stdin is treated special in WIN32. Waiting for stdin is done * Stdin is treated special in WIN32. Waiting for stdin is done
* via a bounce_full event which is set in the stdin thread. * via a bounce_full event which is set in the stdin thread.
* Execute command file reading is done via handle. Execute * Execute command file reading is done via handle.
* command is read via CreateFile/ReadFile instead open/read * WaitForMultipleObjects will only respond with one object,
* because a file descriptor is not waitable.
* WaitForMultipleObjects will only respond with one object
* so an additonal select is also done to determine * so an additonal select is also done to determine
* which individual events are active for the sock. * which individual events are active.
*/ */
static int static int
w32_select(int nfds, fd_set *rdfd, fd_set *wrfd, fd_set *errfd, struct timeval* time) w32_select(int nfds, fd_set *rdfd, fd_set *wrfd, fd_set *errfd, struct timeval* time)
{ {
HANDLE handles[3]; HANDLE handles[3];
SOCKET sock; SOCKET sock;
int inp, result, s_result, num_handles; int inp, sockfd, result, s_result, num_handles;
struct timeval tv_time = {0, 0}; struct timeval tv_time = {0, 0};
fd_set rdfd2; fd_set rdsock, wrsock;
switch (rdfd->fd_count) { switch (rdfd->fd_count) {
case 1: case 1:
inp = -1; inp = -1;
sock = rdfd->fd_array[0]; sockfd = rdfd->fd_array[0];
break; break;
case 2: case 2:
inp = rdfd->fd_array[0]; inp = rdfd->fd_array[0];
sock = rdfd->fd_array[1]; sockfd = rdfd->fd_array[1];
break; break;
default: default:
assert(0); assert(0);
} }
sock = W32_FD_TO_SOCKET(sockfd);
assert(wrfd->fd_count == 0 assert(wrfd->fd_count == 0
|| (wrfd->fd_count == 1 && wrfd->fd_array[0] == sock)); || (wrfd->fd_count == 1 && wrfd->fd_array[0] == (SOCKET)sockfd));
assert(inp < 0 || inp == input_fd); assert(inp < 0 || inp == input_fd);
num_handles = 0; num_handles = 0;
handles[num_handles++] = ctrl_c_event; handles[num_handles++] = ctrl_c_event;
if (inp >= 0) if (inp >= 0)
handles[num_handles++] = inp ? (HANDLE)inp : bounce_full; handles[num_handles++]
= inp ? (HANDLE)_get_osfhandle(inp) : bounce_full;
/* always wait on the socket */ /* always wait on the socket */
handles[num_handles++] = WSACreateEvent(); handles[num_handles++] = WSACreateEvent();
@ -232,20 +237,27 @@ w32_select(int nfds, fd_set *rdfd, fd_set *wrfd, fd_set *errfd, struct timeval*
return -1; return -1;
} }
FD_ZERO(&rdfd2); FD_ZERO(&rdsock);
FD_SET(sock, &rdfd2); FD_ZERO(&wrsock);
s_result = select(sock + 1, &rdfd2, wrfd, NULL, &tv_time); FD_SET(sock, &rdsock);
if (wrfd->fd_count)
FD_SET(sock, &wrsock);
s_result = select(sock + 1, &rdsock, &wrsock, NULL, &tv_time);
if (s_result < 0) { if (s_result < 0) {
errno = WSAGetLastError(); w32_set_winsock_errno();
return s_result; return s_result;
} }
*rdfd = rdfd2; if (!FD_ISSET(sock, &rdsock))
if (inp >= 0 && result == WAIT_OBJECT_0 + 1) { FD_CLR((SOCKET)sockfd, rdfd);
FD_SET((SOCKET)inp, rdfd); if (!FD_ISSET(sock, &wrsock))
FD_CLR((SOCKET)sockfd, wrfd);
if (inp >= 0 && result == WAIT_OBJECT_0 + 1)
s_result++; s_result++;
} else
FD_CLR((SOCKET)inp, rdfd);
return s_result; return s_result;
} }
@ -282,7 +294,6 @@ w32_ring_from_file_or_bounce_buf(struct ring *r, int fd)
return res; return res;
} }
#define ring_from_file w32_ring_from_file_or_bounce_buf #define ring_from_file w32_ring_from_file_or_bounce_buf
#define close(fd) w32_close_handle((fd))
#define read(sock, buffer, buf_size) \ #define read(sock, buffer, buf_size) \
w32_recv((sock), (buffer), (buf_size), 0) w32_recv((sock), (buffer), (buf_size), 0)
#define select(nfds, rd, wr, error, time) \ #define select(nfds, rd, wr, error, time) \

View file

@ -28,7 +28,7 @@
* ringbuf.c: Simple ring buffer * ringbuf.c: Simple ring buffer
* *
* Known contributors to this file: * Known contributors to this file:
* Markus Armbruster, 2007 * Markus Armbruster, 2007-2009
*/ */
#include <config.h> #include <config.h>
@ -41,7 +41,7 @@
#include <unistd.h> #include <unistd.h>
#else #else
#define readv(fd, iov, iovcnt) \ #define readv(fd, iov, iovcnt) \
w32_readv_handle((fd), (iov), (iovcnt)) w32_readv_fd((fd), (iov), (iovcnt))
#define writev(fd, iov, iovcnt) \ #define writev(fd, iov, iovcnt) \
w32_writev_socket((fd), (iov), (iovcnt)) w32_writev_socket((fd), (iov), (iovcnt))
#endif #endif

View file

@ -31,7 +31,7 @@
* Dave Pare, 1989 * Dave Pare, 1989
* Steve McClure, 1998 * Steve McClure, 1998
* Ron Koenderink, 2005 * Ron Koenderink, 2005
* Markus Armbruster, 2005-2007 * Markus Armbruster, 2005-2009
*/ */
#include <config.h> #include <config.h>
@ -46,13 +46,6 @@
#include "proto.h" #include "proto.h"
#include "secure.h" #include "secure.h"
#ifdef _WIN32
#define open(filename, flags, ...) \
((flags & O_CREAT) \
? w32_openfd((filename), (flags), ## __VA_ARGS__) \
: w32_openhandle((filename), (flags)))
#endif
int eight_bit_clean; int eight_bit_clean;
FILE *auxfp; FILE *auxfp;

View file

@ -29,18 +29,58 @@
* *
* Known contributors to this file: * Known contributors to this file:
* Ron Koenderink, 2007 * Ron Koenderink, 2007
* Markus Armbruster, 2009
*/ */
#ifdef _WIN32 #ifdef _WIN32
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
#include "misc.h" #include "misc.h"
#include "linebuf.h"
#include "ringbuf.h" static int
#include "secure.h" fd_is_socket(int fd, SOCKET *sockp)
{
SOCKET sock;
WSANETWORKEVENTS ev;
sock = W32_FD_TO_SOCKET(fd);
if (sockp)
*sockp = sock;
return WSAEnumNetworkEvents(sock, NULL, &ev) == 0;
}
void
w32_set_winsock_errno(void)
{
int err = WSAGetLastError();
WSASetLastError(0);
/* Map some WSAE* errors to the runtime library's error codes. */
switch (err)
{
case WSA_INVALID_HANDLE:
errno = EBADF;
break;
case WSA_NOT_ENOUGH_MEMORY:
errno = ENOMEM;
break;
case WSA_INVALID_PARAMETER:
errno = EINVAL;
break;
case WSAEWOULDBLOCK:
errno = EAGAIN;
break;
case WSAENAMETOOLONG:
errno = ENAMETOOLONG;
break;
case WSAENOTEMPTY:
errno = ENOTEMPTY;
break;
default:
errno = (err > 10000 && err < 10025) ? err - 10000 : err;
break;
}
}
/* /*
* Get user name in the WIN32 environment * Get user name in the WIN32 environment
@ -50,12 +90,12 @@ w32_getpw(void)
{ {
static char unamebuf[128]; static char unamebuf[128];
static struct passwd pwd; static struct passwd pwd;
long unamesize; DWORD unamesize;
unamesize = sizeof(unamebuf); unamesize = sizeof(unamebuf);
if (GetUserName(unamebuf, &unamesize)) { if (GetUserName(unamebuf, &unamesize)) {
pwd.pw_name = unamebuf; pwd.pw_name = unamebuf;
if ((unamesize <= 0 ) || (strlen(unamebuf) <= 0)) if (unamesize == 0 || strlen(unamebuf) == 0)
pwd.pw_name = "nobody"; pwd.pw_name = "nobody";
} else } else
pwd.pw_name = "nobody"; pwd.pw_name = "nobody";
@ -67,7 +107,7 @@ w32_getpw(void)
* set up stdout to work around bugs * set up stdout to work around bugs
*/ */
void void
sysdep_init(void) w32_sysdep_init(void)
{ {
int err; int err;
WSADATA WsaData; WSADATA WsaData;
@ -92,16 +132,20 @@ sysdep_init(void)
*/ */
#undef socket #undef socket
int int
w32_socket(int family, int sock_type, int protocol) w32_socket(int domain, int type, int protocol)
{ {
SOCKET result; SOCKET sock;
result = socket(family, sock_type, protocol); /*
if (result == INVALID_SOCKET) { * We have to use WSASocket() to create non-overlapped IO sockets.
errno = WSAGetLastError(); * Overlapped IO sockets cannot be used with read/write.
*/
sock = WSASocket(domain, type, protocol, NULL, 0, 0);
if (sock == INVALID_SOCKET) {
w32_set_winsock_errno();
return -1; return -1;
} }
return (int)result; return W32_SOCKET_TO_FD(sock);
} }
/* /*
@ -109,13 +153,15 @@ w32_socket(int family, int sock_type, int protocol)
*/ */
#undef connect #undef connect
int int
w32_connect(int sock, struct sockaddr *addr, int addrlen) w32_connect(int sockfd, const struct sockaddr *addr, int addrlen)
{ {
SOCKET sock = W32_FD_TO_SOCKET(sockfd);
int result; int result;
result = connect(sock, addr, addrlen); result = connect(sock, addr, addrlen);
if (result == SOCKET_ERROR) { if (result == SOCKET_ERROR) {
errno = WSAGetLastError(); /* FIXME map WSAEWOULDBLOCK to EINPROGRESS */
w32_set_winsock_errno();
return -1; return -1;
} }
return result; return result;
@ -126,13 +172,14 @@ w32_connect(int sock, struct sockaddr *addr, int addrlen)
*/ */
#undef recv #undef recv
int int
w32_recv(int socket, char *buffer, size_t buf_size, int flags) w32_recv(int sockfd, void *buffer, size_t buf_size, int flags)
{ {
SOCKET socket = W32_FD_TO_SOCKET(sockfd);
int result; int result;
result = recv(socket, buffer, buf_size, flags); result = recv(socket, buffer, buf_size, flags);
if (result == SOCKET_ERROR) { if (result == SOCKET_ERROR) {
errno = WSAGetLastError(); w32_set_winsock_errno();
return -1; return -1;
} }
return result; return result;
@ -143,10 +190,11 @@ w32_recv(int socket, char *buffer, size_t buf_size, int flags)
* Modelled after the GNU's libc/sysdeps/posix/writev.c * Modelled after the GNU's libc/sysdeps/posix/writev.c
*/ */
ssize_t ssize_t
w32_writev_socket(int fd, const struct iovec *iov, int iovcnt) w32_writev_socket(int sockfd, const struct iovec *iov, int iovcnt)
{ {
SOCKET sock = W32_FD_TO_SOCKET(sockfd);
int i; int i;
unsigned char *buffer, *buffer_location; char *buffer, *buffer_location;
size_t total_bytes = 0; size_t total_bytes = 0;
int bytes_written; int bytes_written;
@ -165,12 +213,10 @@ w32_writev_socket(int fd, const struct iovec *iov, int iovcnt)
buffer_location += iov[i].iov_len; buffer_location += iov[i].iov_len;
} }
bytes_written = send(fd, buffer, total_bytes, 0); bytes_written = send(sock, buffer, total_bytes, 0);
free(buffer); free(buffer);
if (bytes_written == SOCKET_ERROR) { if (bytes_written == SOCKET_ERROR) {
errno = WSAGetLastError(); w32_set_winsock_errno();
return -1; return -1;
} }
return bytes_written; return bytes_written;
@ -180,87 +226,38 @@ w32_writev_socket(int fd, const struct iovec *iov, int iovcnt)
* POSIX compatible send() replacement * POSIX compatible send() replacement
*/ */
int int
w32_send(int socket, char *buffer, size_t buf_size, int flags) w32_send(int sockfd, const void *buffer, size_t buf_size, int flags)
{
int result;
result = send(socket, buffer, buf_size, flags);
if (result == SOCKET_ERROR)
errno = WSAGetLastError();
return result;
}
/*
* POSIX compatible close() replacement specialized to sockets.
*/
int
w32_close_socket(int fd)
{ {
SOCKET socket = W32_FD_TO_SOCKET(sockfd);
int result; int result;
result = closesocket(fd); result = send(socket, buffer, buf_size, flags);
if (result == SOCKET_ERROR) if (result == SOCKET_ERROR)
errno = WSAGetLastError(); w32_set_winsock_errno();
return result; return result;
} }
/* /*
* POSIX compatible open() replacement * POSIX compatible close() replacement
*/ */
int int
w32_openfd(const char *fname, int oflag, ...) w32_close(int fd)
{ {
va_list ap; SOCKET sock;
int pmode = 0;
int fd;
int create_permission = 0;
if (oflag & O_CREAT) { if (fd_is_socket(fd, &sock)) {
va_start(ap, oflag); if (closesocket(sock)) {
pmode = va_arg(ap, int); w32_set_winsock_errno();
va_end(ap);
if (pmode & 0400)
create_permission |= _S_IREAD;
if (pmode & 0200)
create_permission |= _S_IWRITE;
}
fd = _open(fname, oflag, create_permission);
return fd;
}
/*
* Open a file for reading, return its handle.
* This can be used in place of open() when a handle is desired for
* waiting on it with WaitForMultipleObjects() or similar.
* Ensure the handle is not zero in order to prevent a problem
* input_fd.
*/
int
w32_openhandle(const char *fname, int oflag)
{
HANDLE handle;
handle = CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE) {
errno = GetLastError();
return -1;
}
if (handle == 0) {
HANDLE dup_handle;
if (!DuplicateHandle(GetCurrentProcess(), handle,
GetCurrentProcess(), &dup_handle,
0, FALSE, DUPLICATE_SAME_ACCESS)) {
errno = GetLastError();
return -1; return -1;
} else {
CloseHandle(handle);
handle = dup_handle;
} }
/*
* This always fails because the underlying handle is already
* gone, but it closes the fd just fine.
*/
_close(fd);
return 0;
} }
return (int)handle; return _close(fd);
} }
/* /*
@ -268,12 +265,12 @@ w32_openhandle(const char *fname, int oflag)
* Modelled after the GNU's libc/sysdeps/posix/readv.c * Modelled after the GNU's libc/sysdeps/posix/readv.c
*/ */
ssize_t ssize_t
w32_readv_handle(int fd, const struct iovec *iov, int iovcnt) w32_readv_fd(int fd, const struct iovec *iov, int iovcnt)
{ {
int i; int i;
unsigned char *buffer, *buffer_location; char *buffer, *buffer_location;
size_t total_bytes = 0; size_t total_bytes = 0;
DWORD bytes_read; int bytes_read;
size_t bytes_left; size_t bytes_left;
for (i = 0; i < iovcnt; i++) { for (i = 0; i < iovcnt; i++) {
@ -286,11 +283,9 @@ w32_readv_handle(int fd, const struct iovec *iov, int iovcnt)
return -1; return -1;
} }
if (!ReadFile((HANDLE)fd, buffer, total_bytes, &bytes_read, NULL)) { bytes_read = read(fd, buffer, total_bytes);
free(buffer); if (bytes_read < 0)
errno = GetLastError();
return -1; return -1;
}
bytes_left = bytes_read; bytes_left = bytes_read;
buffer_location = buffer; buffer_location = buffer;
@ -306,23 +301,7 @@ w32_readv_handle(int fd, const struct iovec *iov, int iovcnt)
} }
free(buffer); free(buffer);
return bytes_read; return bytes_read;
} }
/*
* POSIX compatible close() replacement specialized to files.
* Hack: expects a handle, cannot be used with a file descriptor.
*/
int
w32_close_handle(int fd)
{
int result;
result = CloseHandle((HANDLE)fd);
if (!result)
errno = GetLastError();
return result;
}
#endif /* _WIN32 */ #endif /* _WIN32 */

View file

@ -29,13 +29,13 @@
* *
* Known contributors to this file: * Known contributors to this file:
* Ron Koenderink, 2007 * Ron Koenderink, 2007
* Markus Armbruster, 2009
*/ */
#ifndef _SYSDEF_W32_H #ifndef _SYSDEF_W32_H
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <windows.h> #include <windows.h>
#include <signal.h>
extern int getopt(int, char * const[], const char *); extern int getopt(int, char * const[], const char *);
extern char *optarg; extern char *optarg;
@ -54,35 +54,32 @@ struct iovec {
size_t iov_len; size_t iov_len;
}; };
struct sigaction { #define W32_FD_TO_SOCKET(fd) ((SOCKET)_get_osfhandle((fd)))
int sa_flags; #define W32_SOCKET_TO_FD(fh) (_open_osfhandle((long)(fh), O_RDWR | O_BINARY))
void (*sa_handler)(int sig);
};
extern int w32_recv(int socket, char *buffer, extern void w32_set_winsock_errno(void);
size_t buf_size, int flags);
extern int w32_send(int socket, char *buffer, extern int w32_recv(int sockfd, void *, size_t, int flags);
size_t buf_size, int flags); extern int w32_send(int sockfd, const void *, size_t, int flags);
extern int w32_close_socket(int fd); extern int w32_close(int fd);
extern int w32_socket(int family, int sock_type, int protocol); extern int w32_socket(int domain, int type, int protocol);
extern int w32_connect(int sock, struct sockaddr *addr, int addrlen); extern int w32_connect(int sockfd, const struct sockaddr *, int addrlen);
extern int w32_close_handle(int fd); extern ssize_t w32_readv_fd(int fd, const struct iovec *iov,
extern ssize_t w32_readv_handle(int fd, const struct iovec *iov, int iovcnt);
int iovcnt); extern ssize_t w32_writev_socket(int sockfd, const struct iovec *iov,
extern ssize_t w32_writev_socket(int fd, const struct iovec *iov,
int iovcnt); int iovcnt);
extern int w32_openfd(const char *fname, int oflag, ...);
extern int w32_openhandle(const char *fname, int oflag);
extern struct passwd *w32_getpw(void); extern struct passwd *w32_getpw(void);
extern void sysdep_init(void); extern void w32_sysdep_init(void);
#define recv(sock, buffer, buf_size, flags) \ #define recv(sockfd, buffer, buf_size, flags) \
w32_recv((sock), (buffer), (buf_size), (flags)) w32_recv((sockfd), (buffer), (buf_size), (flags))
#define socket(family, sock_type, protocol) \ #define close(fd) \
w32_socket((family), (sock_type), (protocol)) w32_close((fd))
#define connect(sock, addr, addrlen) \ #define socket(domain, type, protocol) \
w32_connect((sock), (addr), (addrlen)) w32_socket((domain), (type), (protocol))
#define connect(sockfd, addr, addrlen) \
w32_connect((sockfd), (addr), (addrlen))
#define pclose _pclose #define pclose _pclose
#define popen _popen #define popen _popen