2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * See files README, COPYING and CREDITS in the root of the source
23 * tree for related information and legal notices. It is expected
24 * that future projects/authors will amend these files as needed.
28 * posixio.c: POSIX IO emulation layer for WIN32
30 * Known contributors to this file:
31 * Ron Koenderink, 2007
32 * Markus Armbruster, 2007-2008
36 * POSIX has just one kind of file descriptors, while Windows has (at
37 * least) two: one for sockets and one for files, with separate
38 * functions to operate on them. To present a more POSIX-like
39 * interface to our application code, we provide a compatibility layer
40 * that maps POSIX file descriptors to sockets and file handles behind
41 * the scenes. This actual mapping is done by the fdmap. It doesn't
42 * implement the finer points of POSIX correctly. In particular, the
43 * actual values of the file descriptors usually differ.
55 * Need to include winsock2.h before ws2tcpip.h.
56 * Use sys/socket.h to ensure the #undef NS_ALL
57 * is not missed after including winsock2.h.
59 #include "sys/socket.h"
68 * FD_SETSIZE is the size for the maximum number of sockets.
69 * The number of file descriptors is in a variable _nhandle
70 * based on the assertion output. In order the simplify the
71 * code and skip dynamic allocation, used double the socket size.
73 #define MAX_FDS (FD_SETSIZE * 2)
79 FDMAP_IO_ANY /* used for searching only (lookup_handle) */
84 enum fdmap_io_type type;
87 static struct fdmap fdmap[MAX_FDS] = {
95 * Allocate a POSIX equivalent file descriptor.
96 * Note once get_fd() is called either free_fd() or set_fd()
97 * must be called before thread mutex is released as the
98 * allocation/deallocation code is not thread safe.
105 for (fd = 0; fd < nfd && fdmap[fd].type != FDMAP_IO_NOTUSED; fd++) ;
111 fdmap[fd].type = FDMAP_IO_NOTUSED;
118 * Deallocate a POSIX equivalent file descriptor.
123 fdmap[fd].type = FDMAP_IO_NOTUSED;
124 for(; fdmap[nfd - 1].type == FDMAP_IO_NOTUSED; nfd--) ;
128 * Complete the allocation of the file descriptor.
131 set_fd(int fd, enum fdmap_io_type type, int handle)
135 fdmap[fd].handle = handle;
136 fdmap[fd].type = type;
139 * Garbage collection for fileno(), currently not
140 * replacing fclose() and fcloseall() so do not know when
141 * a stream is closed.
143 for (i = 0; i < nfd; i++) {
144 if (i != fd && type == fdmap[i].type && handle == fdmap[i].handle)
150 * Find the windows handle (file or socket) for file descriptor.
151 * Return windows handle and type of handle.
152 * You can search for a specific type (FDMAP_IO_FILE or FDMAP_IO_SOCKET)
153 * or for both search by using FDMAP_IO_ANY.
154 * FDMAP_IO_NOTUSED is not valid type to search with.
157 lookup_handle(int fd, enum fdmap_io_type d_type, int error,
158 enum fdmap_io_type *type_ptr, int *handle_ptr)
161 if (fd < 0 || fd >= MAX_FDS) {
165 } else if ((fdmap[fd].type != d_type && d_type != FDMAP_IO_ANY) ||
166 (fdmap[fd].type == FDMAP_IO_NOTUSED && d_type == FDMAP_IO_ANY)) {
171 if (type_ptr != NULL)
172 *type_ptr = fdmap[fd].type;
173 if (handle_ptr != NULL)
174 *handle_ptr = fdmap[fd].handle;
179 * Find and return the file descriptor associated with windows handle.
180 * You can search for FDMAP_IO_FILE or FDMAP_IO_SOCKET.
181 * FDMAP_IO_ANY or FDMAP_IO_NOTUSED is not considered valid search
185 lookup_fd(int handle, enum fdmap_io_type d_type)
189 for (i = 0; i < nfd; i++)
190 if (fdmap[i].handle == handle && fdmap[i].type == d_type)
196 * Get the window socket handle for POSIX file descriptor.
199 posix_fd2socket(int fd)
202 enum fdmap_io_type type;
204 if (!lookup_handle(fd, FDMAP_IO_SOCKET, WSAENOTSOCK,
206 return INVALID_SOCKET;
210 #define SOCKET_FUNCTION(expr) \
214 if (!lookup_handle(fd, FDMAP_IO_SOCKET, \
215 ENOTSOCK, NULL, &handle)) \
219 if (result == SOCKET_ERROR) { \
220 errno = WSAGetLastError(); \
226 * POSIX equivalent for accept().
230 posix_accept(int fd, struct sockaddr *addr, socklen_t *addrlen)
236 if (!lookup_handle(fd, FDMAP_IO_SOCKET, ENOTSOCK, NULL, &handle))
243 new_handle = accept(handle, addr, addrlen);
244 if (new_handle == INVALID_SOCKET) {
246 errno = WSAGetLastError();
249 set_fd(new_fd, FDMAP_IO_SOCKET, (int)new_handle);
254 * POSIX equivalent for bind().
258 posix_bind(int fd, const struct sockaddr *name, socklen_t namelen)
260 SOCKET_FUNCTION(bind(handle, name, namelen))
264 * POSIX equivalent for listen().
268 posix_listen(int fd, int backlog)
270 SOCKET_FUNCTION(listen(handle, backlog))
274 * POSIX equivalent for setsockopt().
278 posix_setsockopt(int fd, int level, int optname,
279 const void *optval, socklen_t optlen)
282 * SO_REUSEADDR requests to permit another bind even when the
283 * port is still in state TIME_WAIT. Windows' SO_REUSEADDR is
284 * broken: it makes bind() succeed no matter what, even if
285 * there's another server running on the same port. Luckily,
286 * bind() seems to be broken as well: it seems to succeed while
287 * the port is in state TIME_WAIT by default; thus we get the
288 * behavior we want by not setting SO_REUSEADDR.
290 if (level == SOL_SOCKET && optname == SO_REUSEADDR)
293 SOCKET_FUNCTION(setsockopt(handle, level, optname,
299 * POSIX equivalent for shutdown().
303 posix_shutdown(int fd, int how)
305 SOCKET_FUNCTION(shutdown(handle, how))
309 * POSIX equivalent for socket().
313 posix_socket(int domain, int type, int protocol)
318 if ((new_fd = get_fd()) < 0)
321 handle = socket(domain, type, protocol);
322 if (handle == INVALID_SOCKET) {
324 errno = WSAGetLastError();
327 set_fd(new_fd, FDMAP_IO_SOCKET, (int)handle);
331 #ifdef HAVE_GETADDRINFO
333 inet_ntop(int af, const void *src, char *dst, socklen_t len)
336 struct sockaddr_in sin;
337 struct sockaddr_in6 sin6;
341 memset(&sin, 0, sizeof(sin));
343 memcpy(&sin.sin_addr, src, sizeof(sin.sin_addr));
344 sa = (struct sockaddr *)&sin;
346 } else if (af == AF_INET6) {
347 memset(&sin6, 0, sizeof(sin6));
348 sin6.sin6_family = af;
349 memcpy(&sin6.sin6_addr, src, sizeof(sin6.sin6_addr));
350 sa = (struct sockaddr *)&sin6;
351 salen = sizeof(sin6);
353 errno = EAFNOSUPPORT;
357 if (getnameinfo(sa, salen, dst, len, NULL, 0, NI_NUMERICHOST)) {
358 errno = EAFNOSUPPORT;
366 #define FILE_FUNCTION(type, expr) \
369 if (!lookup_handle(fd, (type), EBADF, NULL, &handle)) \
375 * POSIX equivalent for close().
382 enum fdmap_io_type type;
384 if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
389 case FDMAP_IO_SOCKET:
390 result = closesocket(handle);
391 if (result == SOCKET_ERROR) {
392 errno = WSAGetLastError();
397 return _close(handle);
405 * posix_fsync forces file sync with the disk.
406 * In order for the power report report to accurate timestamp,
407 * the _commit() is to force a sync with disk and therefore
408 * an update for file time.
413 FILE_FUNCTION(FDMAP_IO_FILE, _commit(handle))
420 ftruncate(int fd, off_t length)
422 FILE_FUNCTION(FDMAP_IO_FILE, _chsize(handle, length))
426 * POSIX equivalent for fstat().
427 * fstat() is used instead of _fstat(),
428 * otherwise problems with the 32/64 time definitions
433 posix_fstat(int fd, struct stat *buffer)
435 FILE_FUNCTION(FDMAP_IO_ANY, fstat(handle, buffer))
439 * POSIX equivalent for lseek().
442 posix_lseek(int fd, off_t offset, int origin)
444 FILE_FUNCTION(FDMAP_IO_FILE, _lseek(handle, offset, origin))
448 * POSIX equivalent for open().
449 * Implements file locks when opening files to provide equivalent
453 posix_open(const char *fname, int oflag, ...)
456 int pmode = 0, new_fd;
459 if (oflag & O_CREAT) {
461 pmode = va_arg(ap, int);
465 if ((new_fd = get_fd()) < 0)
469 * We don't implement fcntl() for F_SETLK. Instead, we lock *all*
470 * files we open. Not ideal, but it works for Empire.
472 handle = _sopen(fname, oflag,
473 oflag & O_RDONLY ? SH_DENYNO : SH_DENYWR, pmode);
478 set_fd(new_fd, FDMAP_IO_FILE, handle);
482 #define SHARED_FUNCTION(socket_expr, file_expr) \
485 enum fdmap_io_type type; \
487 if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle)) \
491 case FDMAP_IO_SOCKET: \
492 result = (socket_expr); \
493 if (result == SOCKET_ERROR) { \
494 errno = WSAGetLastError(); \
498 case FDMAP_IO_FILE: \
499 return (file_expr); \
506 * POSIX equivalent for read().
509 posix_read(int fd, void *buffer, size_t count)
511 SHARED_FUNCTION(recv(handle, buffer, count, 0),
512 _read(handle, buffer, count))
516 * POSIX equivalent for readv
517 * Modelled after the GNU's libc/sysdeps/posix/readv.c
520 readv(int fd, const struct iovec *iov, int iovcnt)
523 unsigned char *buffer, *buffer_location;
524 size_t total_bytes = 0;
528 for (i = 0; i < iovcnt; i++) {
529 total_bytes += iov[i].iov_len;
532 buffer = malloc(total_bytes);
533 if (buffer == NULL && total_bytes != 0) {
538 bytes_read = posix_read(fd, buffer, total_bytes);
539 if (bytes_read <= 0) {
544 bytes_left = bytes_read;
545 buffer_location = buffer;
546 for (i = 0; i < iovcnt; i++) {
547 size_t copy = MIN(iov[i].iov_len, bytes_left);
549 memcpy(iov[i].iov_base, buffer_location, copy);
551 buffer_location += copy;
563 * POSIX equivalent for write().
566 posix_write(int fd, const void *buffer, size_t count)
568 SHARED_FUNCTION(send(handle, buffer, count, 0),
569 _write(handle, buffer, count))
573 * POSIX equivalent for writev
574 * Modelled after the GNU's libc/sysdeps/posix/writev.c
577 writev(int fd, const struct iovec *iov, int iovcnt)
580 unsigned char *buffer, *buffer_location;
581 size_t total_bytes = 0;
584 for (i = 0; i < iovcnt; i++)
585 total_bytes += iov[i].iov_len;
587 buffer = malloc(total_bytes);
588 if (buffer == NULL && total_bytes != 0) {
593 buffer_location = buffer;
594 for (i = 0; i < iovcnt; i++) {
595 memcpy(buffer_location, iov[i].iov_base, iov[i].iov_len);
596 buffer_location += iov[i].iov_len;
599 bytes_written = posix_write(fd, buffer, total_bytes);
603 if (bytes_written <= 0)
605 return bytes_written;
609 * POSIX equivalent for fileno().
610 * As fopen/fclose/fcloseall are not implemented as POSIX
611 * equivalent functions, the mapping is done when required
612 * by a call to fileno(). The garbage collection of the
613 * file descriptors allocated is done in set_fd() when the
622 if (stream == NULL) {
627 handle = _fileno(stream);
629 fd = lookup_fd(handle, FDMAP_IO_FILE);
633 if ((fd = get_fd()) < 0) {
638 set_fd(fd, FDMAP_IO_FILE, handle);
643 * POSIX equivalent for fcntl().
644 * Currently supports only the F_GETFL/F_SETFL/O_NONBLOCK
645 * Currently ignores F_GETLK/F_SETLK as the file locks are
646 * implement in open()
649 fcntl(int fd, int cmd, ...)
653 unsigned int nonblocking;
657 enum fdmap_io_type type;
659 if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
666 * F_GETFL and F_SETFL only support O_NONBLOCK
667 * for sockets currently
669 if (type == FDMAP_IO_SOCKET) {
670 result = WSAIoctl(handle, FIONBIO, NULL, 0,&nonblocking,
671 sizeof (nonblocking), &bytes_returned, NULL, NULL);
674 errno = WSAGetLastError();
685 if (type == FDMAP_IO_SOCKET) {
687 value = va_arg(ap, int);
689 if (value & O_NONBLOCK)
694 result = WSAIoctl(handle, FIONBIO, &nonblocking,
695 sizeof (nonblocking), NULL, 0, &bytes_returned,
699 errno = WSAGetLastError();
707 * The POSIX equivalent is not available in WIN32
708 * That implement the file locking in the file open
709 * by using sopen instead of open.