From 772991af40e139814368762d4075be324bfdab77 Mon Sep 17 00:00:00 2001 From: Mike Pape Date: Thu, 4 Nov 2010 02:35:09 +0100 Subject: mingw: add network-wrappers for daemon git-daemon requires some socket-functionality that is not yet supported in the Windows-port. This patch adds said functionality, and makes sure WSAStartup gets called by socket(), since it is the first network-call in git-daemon. Signed-off-by: Mike Pape Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/mingw.c | 44 +++++++++++++++++++++++++++++++++++++++++++- compat/mingw.h | 16 ++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 6590f33cc8..701a555f13 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1175,7 +1175,10 @@ int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen, int mingw_socket(int domain, int type, int protocol) { int sockfd; - SOCKET s = WSASocket(domain, type, protocol, NULL, 0, 0); + SOCKET s; + + ensure_socket_initialization(); + s = WSASocket(domain, type, protocol, NULL, 0, 0); if (s == INVALID_SOCKET) { /* * WSAGetLastError() values are regular BSD error codes @@ -1205,6 +1208,45 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz) return connect(s, sa, sz); } +#undef bind +int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz) +{ + SOCKET s = (SOCKET)_get_osfhandle(sockfd); + return bind(s, sa, sz); +} + +#undef setsockopt +int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen) +{ + SOCKET s = (SOCKET)_get_osfhandle(sockfd); + return setsockopt(s, lvl, optname, (const char*)optval, optlen); +} + +#undef listen +int mingw_listen(int sockfd, int backlog) +{ + SOCKET s = (SOCKET)_get_osfhandle(sockfd); + return listen(s, backlog); +} + +#undef accept +int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz) +{ + int sockfd2; + + SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1); + SOCKET s2 = accept(s1, sa, sz); + + /* convert into a file descriptor */ + if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) { + int err = errno; + closesocket(s2); + return error("unable to make a socket file descriptor: %s", + strerror(err)); + } + return sockfd2; +} + #undef rename int mingw_rename(const char *pold, const char *pnew) { diff --git a/compat/mingw.h b/compat/mingw.h index 83e35e833b..a5bde82b9a 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -7,6 +7,7 @@ typedef int pid_t; typedef int uid_t; +typedef int socklen_t; #define hstrerror strerror #define S_IFLNK 0120000 /* Symbolic link */ @@ -47,6 +48,9 @@ typedef int uid_t; #define F_SETFD 2 #define FD_CLOEXEC 0x1 +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define ECONNABORTED WSAECONNABORTED + struct passwd { char *pw_name; char *pw_gecos; @@ -225,6 +229,18 @@ int mingw_socket(int domain, int type, int protocol); int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz); #define connect mingw_connect +int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz); +#define bind mingw_bind + +int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen); +#define setsockopt mingw_setsockopt + +int mingw_listen(int sockfd, int backlog); +#define listen mingw_listen + +int mingw_accept(int sockfd, struct sockaddr *sa, socklen_t *sz); +#define accept mingw_accept + int mingw_rename(const char*, const char*); #define rename mingw_rename -- cgit v1.2.3 From 088d880247dc7bb1a27f07e320bab7f9b63e06bc Mon Sep 17 00:00:00 2001 From: Mike Pape Date: Thu, 4 Nov 2010 02:35:10 +0100 Subject: mingw: implement syslog Syslog does not usually exist on Windows, so implement our own using Window's ReportEvent mechanism. Strings containing "%1" gets expanded into them selves by ReportEvent, resulting in an unreadable string. "%2" and above is not a problem. Unfortunately, on Windows an IPv6 address can contain "%1", so expand "%1" to "% 1" before reporting. "%%1" is also a problem for ReportEvent, but that string cannot occur in an IPv6 address. Signed-off-by: Mike Pape Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/win32/syslog.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++ compat/win32/syslog.h | 20 ++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 compat/win32/syslog.c create mode 100644 compat/win32/syslog.h (limited to 'compat') diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c new file mode 100644 index 0000000000..42b95a9b51 --- /dev/null +++ b/compat/win32/syslog.c @@ -0,0 +1,72 @@ +#include "../../git-compat-util.h" +#include "../../strbuf.h" + +static HANDLE ms_eventlog; + +void openlog(const char *ident, int logopt, int facility) +{ + if (ms_eventlog) + return; + + ms_eventlog = RegisterEventSourceA(NULL, ident); + + if (!ms_eventlog) + warning("RegisterEventSource() failed: %lu", GetLastError()); +} + +void syslog(int priority, const char *fmt, ...) +{ + struct strbuf sb = STRBUF_INIT; + struct strbuf_expand_dict_entry dict[] = { + {"1", "% 1"}, + {NULL, NULL} + }; + WORD logtype; + char *str; + int str_len; + va_list ap; + + if (!ms_eventlog) + return; + + va_start(ap, fmt); + str_len = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (str_len < 0) { + warning("vsnprintf failed: '%s'", strerror(errno)); + return; + } + + str = malloc(str_len + 1); + va_start(ap, fmt); + vsnprintf(str, str_len + 1, fmt, ap); + va_end(ap); + strbuf_expand(&sb, str, strbuf_expand_dict_cb, &dict); + free(str); + + switch (priority) { + case LOG_EMERG: + case LOG_ALERT: + case LOG_CRIT: + case LOG_ERR: + logtype = EVENTLOG_ERROR_TYPE; + break; + + case LOG_WARNING: + logtype = EVENTLOG_WARNING_TYPE; + break; + + case LOG_NOTICE: + case LOG_INFO: + case LOG_DEBUG: + default: + logtype = EVENTLOG_INFORMATION_TYPE; + break; + } + + ReportEventA(ms_eventlog, logtype, 0, 0, NULL, 1, 0, + (const char **)&sb.buf, NULL); + + strbuf_release(&sb); +} diff --git a/compat/win32/syslog.h b/compat/win32/syslog.h new file mode 100644 index 0000000000..70daa7c08b --- /dev/null +++ b/compat/win32/syslog.h @@ -0,0 +1,20 @@ +#ifndef SYSLOG_H +#define SYSLOG_H + +#define LOG_PID 0x01 + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +#define LOG_DAEMON (3<<3) + +void openlog(const char *ident, int logopt, int facility); +void syslog(int priority, const char *fmt, ...); + +#endif /* SYSLOG_H */ -- cgit v1.2.3 From da523cc597b1be7fe61ee870cf93b142e70fab7f Mon Sep 17 00:00:00 2001 From: Mike Pape Date: Thu, 4 Nov 2010 02:35:11 +0100 Subject: compat: add inet_pton and inet_ntop prototypes Windows doesn't have inet_pton and inet_ntop, so add prototypes in git-compat-util.h for them. At the same time include git-compat-util.h in the sources for these functions, so they use the network-wrappers from there on Windows. Signed-off-by: Mike Pape Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/inet_ntop.c | 6 +++--- compat/inet_pton.c | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'compat') diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c index f44498258d..e5b46a0f21 100644 --- a/compat/inet_ntop.c +++ b/compat/inet_ntop.c @@ -17,9 +17,9 @@ #include #include -#include -#include -#include + +#include "../git-compat-util.h" + #include #include diff --git a/compat/inet_pton.c b/compat/inet_pton.c index 4078fc0877..2ec995e63d 100644 --- a/compat/inet_pton.c +++ b/compat/inet_pton.c @@ -17,9 +17,9 @@ #include #include -#include -#include -#include + +#include "../git-compat-util.h" + #include #include @@ -41,7 +41,9 @@ */ static int inet_pton4(const char *src, unsigned char *dst); +#ifndef NO_IPV6 static int inet_pton6(const char *src, unsigned char *dst); +#endif /* int * inet_pton4(src, dst) -- cgit v1.2.3 From bb34c5aabe9f8d86a40d5ab8518fc78602264179 Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 4 Nov 2010 02:35:12 +0100 Subject: inet_ntop: fix a couple of old-style decls Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/inet_ntop.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'compat') diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c index e5b46a0f21..ea249c6ac6 100644 --- a/compat/inet_ntop.c +++ b/compat/inet_ntop.c @@ -50,10 +50,7 @@ * Paul Vixie, 1996. */ static const char * -inet_ntop4(src, dst, size) - const u_char *src; - char *dst; - size_t size; +inet_ntop4(const u_char *src, char *dst, size_t size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[sizeof "255.255.255.255"]; @@ -78,10 +75,7 @@ inet_ntop4(src, dst, size) * Paul Vixie, 1996. */ static const char * -inet_ntop6(src, dst, size) - const u_char *src; - char *dst; - size_t size; +inet_ntop6(const u_char *src, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough @@ -178,11 +172,7 @@ inet_ntop6(src, dst, size) * Paul Vixie, 1996. */ const char * -inet_ntop(af, src, dst, size) - int af; - const void *src; - char *dst; - size_t size; +inet_ntop(int af, const void *src, char *dst, size_t size) { switch (af) { case AF_INET: -- cgit v1.2.3 From 52de4db57977175c5ad1d6c3922e772d6de08496 Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 4 Nov 2010 02:35:13 +0100 Subject: mingw: use real pid The Windows port have so far been using process handles in place of PID. However, this is not work consistent with what getpid returns. PIDs are system-global identifiers, but process handles are local to a process. Using PIDs instead of process handles allows, for instance, a user to kill a hung process with the Task Manager, something that would have been impossible with process handles. Change the code to use the real PID, and use OpenProcess to get a process-handle. Store the PID and the process handle in a linked list protected by a critical section, so we can safely close the process handle later. Linked list code written by Pat Thoyts. Signed-off-by: Erik Faye-Lund Signed-off-by: Pat Thoyts Signed-off-by: Junio C Hamano --- compat/mingw.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- compat/mingw.h | 10 +++----- 2 files changed, 75 insertions(+), 8 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 701a555f13..736d03fa0c 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -702,6 +702,14 @@ static int env_compare(const void *a, const void *b) return strcasecmp(*ea, *eb); } +struct pinfo_t { + struct pinfo_t *next; + pid_t pid; + HANDLE proc; +} pinfo_t; +struct pinfo_t *pinfo = NULL; +CRITICAL_SECTION pinfo_cs; + static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, const char *dir, int prepend_cmd, int fhin, int fhout, int fherr) @@ -794,7 +802,26 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, return -1; } CloseHandle(pi.hThread); - return (pid_t)pi.hProcess; + + /* + * The process ID is the human-readable identifier of the process + * that we want to present in log and error messages. The handle + * is not useful for this purpose. But we cannot close it, either, + * because it is not possible to turn a process ID into a process + * handle after the process terminated. + * Keep the handle in a list for waitpid. + */ + EnterCriticalSection(&pinfo_cs); + { + struct pinfo_t *info = xmalloc(sizeof(struct pinfo_t)); + info->pid = pi.dwProcessId; + info->proc = pi.hProcess; + info->next = pinfo; + pinfo = info; + } + LeaveCriticalSection(&pinfo_cs); + + return (pid_t)pi.dwProcessId; } static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env, @@ -1518,6 +1545,50 @@ char *getpass(const char *prompt) return strbuf_detach(&buf, NULL); } +pid_t waitpid(pid_t pid, int *status, unsigned options) +{ + HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, + FALSE, pid); + if (!h) { + errno = ECHILD; + return -1; + } + + if (options == 0) { + struct pinfo_t **ppinfo; + if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { + CloseHandle(h); + return 0; + } + + if (status) + GetExitCodeProcess(h, (LPDWORD)status); + + EnterCriticalSection(&pinfo_cs); + + ppinfo = &pinfo; + while (*ppinfo) { + struct pinfo_t *info = *ppinfo; + if (info->pid == pid) { + CloseHandle(info->proc); + *ppinfo = info->next; + free(info); + break; + } + ppinfo = &info->next; + } + + LeaveCriticalSection(&pinfo_cs); + + CloseHandle(h); + return pid; + } + CloseHandle(h); + + errno = EINVAL; + return -1; +} + #ifndef NO_MINGW_REPLACE_READDIR /* MinGW readdir implementation to avoid extra lstats for Git */ struct mingw_DIR diff --git a/compat/mingw.h b/compat/mingw.h index a5bde82b9a..7c4eeea38c 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -140,13 +140,7 @@ static inline int mingw_unlink(const char *pathname) } #define unlink mingw_unlink -static inline pid_t waitpid(pid_t pid, int *status, unsigned options) -{ - if (options == 0) - return _cwait(status, pid, 0); - errno = EINVAL; - return -1; -} +pid_t waitpid(pid_t pid, int *status, unsigned options); #ifndef NO_OPENSSL #include @@ -321,11 +315,13 @@ void free_environ(char **env); static int mingw_main(); \ int main(int argc, const char **argv) \ { \ + extern CRITICAL_SECTION pinfo_cs; \ _fmode = _O_BINARY; \ _setmode(_fileno(stdin), _O_BINARY); \ _setmode(_fileno(stdout), _O_BINARY); \ _setmode(_fileno(stderr), _O_BINARY); \ argv[0] = xstrdup(_pgmptr); \ + InitializeCriticalSection(&pinfo_cs); \ return mingw_main(argc, argv); \ } \ static int mingw_main(c,v) -- cgit v1.2.3 From ef7108caf3cd200620000921f1b121f8c78d9d3b Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 4 Nov 2010 02:35:14 +0100 Subject: mingw: support waitpid with pid > 0 and WNOHANG Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/mingw.c | 8 ++++++++ compat/mingw.h | 1 + 2 files changed, 9 insertions(+) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 736d03fa0c..47a0e8861d 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1554,6 +1554,14 @@ pid_t waitpid(pid_t pid, int *status, unsigned options) return -1; } + if (pid > 0 && options & WNOHANG) { + if (WAIT_OBJECT_0 != WaitForSingleObject(h, 0)) { + CloseHandle(h); + return 0; + } + options &= ~WNOHANG; + } + if (options == 0) { struct pinfo_t **ppinfo; if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { diff --git a/compat/mingw.h b/compat/mingw.h index 7c4eeea38c..379d7bff2c 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -140,6 +140,7 @@ static inline int mingw_unlink(const char *pathname) } #define unlink mingw_unlink +#define WNOHANG 1 pid_t waitpid(pid_t pid, int *status, unsigned options); #ifndef NO_OPENSSL -- cgit v1.2.3 From 82fc07b7baa6f9e188025cc06a87ebc60a6f2d5e Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 4 Nov 2010 02:35:15 +0100 Subject: mingw: add kill emulation This is a quite limited kill-emulation; it can only handle SIGTERM on positive pids. However, it's enough for git-daemon. Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/mingw.c | 19 +++++++++++++++++++ compat/mingw.h | 3 +++ 2 files changed, 22 insertions(+) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 47a0e8861d..146a6ed79c 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -936,6 +936,25 @@ void mingw_execv(const char *cmd, char *const *argv) mingw_execve(cmd, argv, environ); } +int mingw_kill(pid_t pid, int sig) +{ + if (pid > 0 && sig == SIGTERM) { + HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + + if (TerminateProcess(h, -1)) { + CloseHandle(h); + return 0; + } + + errno = err_win_to_posix(GetLastError()); + CloseHandle(h); + return -1; + } + + errno = EINVAL; + return -1; +} + static char **copy_environ(void) { char **env; diff --git a/compat/mingw.h b/compat/mingw.h index 379d7bff2c..51fca2f845 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -143,6 +143,9 @@ static inline int mingw_unlink(const char *pathname) #define WNOHANG 1 pid_t waitpid(pid_t pid, int *status, unsigned options); +#define kill mingw_kill +int mingw_kill(pid_t pid, int sig); + #ifndef NO_OPENSSL #include static inline int mingw_SSL_set_fd(SSL *ssl, int fd) -- cgit v1.2.3 From cbefd2d6322bff0033fd50bfe175c52b79933571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 4 Nov 2010 02:35:18 +0100 Subject: Improve the mingw getaddrinfo stub to handle more use cases Allow the node parameter to be null, which is used for getting the default bind address. Also allow the hints parameter to be null, to improve standard conformance of the stub implementation a little. Signed-off-by: Martin Storsjo Signed-off-by: Junio C Hamano --- compat/mingw.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 146a6ed79c..a6c1ee702a 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1039,19 +1039,22 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { - struct hostent *h = gethostbyname(node); + struct hostent *h = NULL; struct addrinfo *ai; struct sockaddr_in *sin; - if (!h) - return WSAGetLastError(); + if (node) { + h = gethostbyname(node); + if (!h) + return WSAGetLastError(); + } ai = xmalloc(sizeof(struct addrinfo)); *res = ai; ai->ai_flags = 0; ai->ai_family = AF_INET; - ai->ai_socktype = hints->ai_socktype; - switch (hints->ai_socktype) { + ai->ai_socktype = hints ? hints->ai_socktype : 0; + switch (ai->ai_socktype) { case SOCK_STREAM: ai->ai_protocol = IPPROTO_TCP; break; @@ -1063,14 +1066,25 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service, break; } ai->ai_addrlen = sizeof(struct sockaddr_in); - ai->ai_canonname = strdup(h->h_name); + if (hints && (hints->ai_flags & AI_CANONNAME)) + ai->ai_canonname = h ? strdup(h->h_name) : NULL; + else + ai->ai_canonname = NULL; sin = xmalloc(ai->ai_addrlen); memset(sin, 0, ai->ai_addrlen); sin->sin_family = AF_INET; + /* Note: getaddrinfo is supposed to allow service to be a string, + * which should be looked up using getservbyname. This is + * currently not implemented */ if (service) sin->sin_port = htons(atoi(service)); - sin->sin_addr = *(struct in_addr *)h->h_addr; + if (h) + sin->sin_addr = *(struct in_addr *)h->h_addr; + else if (hints && (hints->ai_flags & AI_PASSIVE)) + sin->sin_addr.s_addr = INADDR_ANY; + else + sin->sin_addr.s_addr = INADDR_LOOPBACK; ai->ai_addr = (struct sockaddr *)sin; ai->ai_next = 0; return 0; -- cgit v1.2.3 From 49521af8a818039f8daf18f2160c4d1e7dbb4270 Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 4 Nov 2010 02:35:20 +0100 Subject: mingw: import poll-emulation from gnulib copy lib/poll.c and lib/poll.in.h verbatim from commit 0a05120 in git://git.savannah.gnu.org/gnulib.git to compat/win32/sys/poll.[ch] To upgrade this code in the future, branch out from this commit, copy new versions of the files above on top, and merge back the result. Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/win32/sys/poll.c | 597 ++++++++++++++++++++++++++++++++++++++++++++++++ compat/win32/sys/poll.h | 53 +++++ 2 files changed, 650 insertions(+) create mode 100644 compat/win32/sys/poll.c create mode 100644 compat/win32/sys/poll.h (limited to 'compat') diff --git a/compat/win32/sys/poll.c b/compat/win32/sys/poll.c new file mode 100644 index 0000000000..092d05e6bd --- /dev/null +++ b/compat/win32/sys/poll.c @@ -0,0 +1,597 @@ +/* Emulation for poll(2) + Contributed by Paolo Bonzini. + + Copyright 2001-2003, 2006-2010 Free Software Foundation, Inc. + + This file is part of gnulib. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Tell gcc not to warn about the (nfd < 0) tests, below. */ +#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__ +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#include +#include + +#include +#include "poll.h" +#include +#include +#include + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# define WIN32_NATIVE +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +#endif + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#include + +#ifndef INFTIM +# define INFTIM (-1) +#endif + +/* BeOS does not have MSG_PEEK. */ +#ifndef MSG_PEEK +# define MSG_PEEK 0 +#endif + +#ifdef WIN32_NATIVE + +#define IsConsoleHandle(h) (((long) (h) & 3) == 3) + +static BOOL +IsSocketHandle (HANDLE h) +{ + WSANETWORKEVENTS ev; + + if (IsConsoleHandle (h)) + return FALSE; + + /* Under Wine, it seems that getsockopt returns 0 for pipes too. + WSAEnumNetworkEvents instead distinguishes the two correctly. */ + ev.lNetworkEvents = 0xDEADBEEF; + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + return ev.lNetworkEvents != 0xDEADBEEF; +} + +/* Declare data structures for ntdll functions. */ +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _IO_STATUS_BLOCK +{ + union { + DWORD Status; + PVOID Pointer; + } u; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef enum _FILE_INFORMATION_CLASS { + FilePipeLocalInformation = 24 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef DWORD (WINAPI *PNtQueryInformationFile) + (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); + +# ifndef PIPE_BUF +# define PIPE_BUF 512 +# endif + +/* Compute revents values for file handle H. If some events cannot happen + for the handle, eliminate them from *P_SOUGHT. */ + +static int +win32_compute_revents (HANDLE h, int *p_sought) +{ + int i, ret, happened; + INPUT_RECORD *irbuffer; + DWORD avail, nbuffer; + BOOL bRet; + IO_STATUS_BLOCK iosb; + FILE_PIPE_LOCAL_INFORMATION fpli; + static PNtQueryInformationFile NtQueryInformationFile; + static BOOL once_only; + + switch (GetFileType (h)) + { + case FILE_TYPE_PIPE: + if (!once_only) + { + NtQueryInformationFile = (PNtQueryInformationFile) + GetProcAddress (GetModuleHandle ("ntdll.dll"), + "NtQueryInformationFile"); + once_only = TRUE; + } + + happened = 0; + if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) + { + if (avail) + happened |= *p_sought & (POLLIN | POLLRDNORM); + } + else if (GetLastError () == ERROR_BROKEN_PIPE) + happened |= POLLHUP; + + else + { + /* It was the write-end of the pipe. Check if it is writable. + If NtQueryInformationFile fails, optimistically assume the pipe is + writable. This could happen on Win9x, where NtQueryInformationFile + is not available, or if we inherit a pipe that doesn't permit + FILE_READ_ATTRIBUTES access on the write end (I think this should + not happen since WinXP SP2; WINE seems fine too). Otherwise, + ensure that enough space is available for atomic writes. */ + memset (&iosb, 0, sizeof (iosb)); + memset (&fpli, 0, sizeof (fpli)); + + if (!NtQueryInformationFile + || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), + FilePipeLocalInformation) + || fpli.WriteQuotaAvailable >= PIPE_BUF + || (fpli.OutboundQuota < PIPE_BUF && + fpli.WriteQuotaAvailable == fpli.OutboundQuota)) + happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); + } + return happened; + + case FILE_TYPE_CHAR: + ret = WaitForSingleObject (h, 0); + if (!IsConsoleHandle (h)) + return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0; + + nbuffer = avail = 0; + bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); + if (bRet) + { + /* Input buffer. */ + *p_sought &= POLLIN | POLLRDNORM; + if (nbuffer == 0) + return POLLHUP; + if (!*p_sought) + return 0; + + irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); + bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); + if (!bRet || avail == 0) + return POLLHUP; + + for (i = 0; i < avail; i++) + if (irbuffer[i].EventType == KEY_EVENT) + return *p_sought; + return 0; + } + else + { + /* Screen buffer. */ + *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND; + return *p_sought; + } + + default: + ret = WaitForSingleObject (h, 0); + if (ret == WAIT_OBJECT_0) + return *p_sought & ~(POLLPRI | POLLRDBAND); + + return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); + } +} + +/* Convert fd_sets returned by select into revents values. */ + +static int +win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents) +{ + int happened = 0; + + if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT) + happened |= (POLLIN | POLLRDNORM) & sought; + + else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) + { + int r, error; + + char data[64]; + WSASetLastError (0); + r = recv (h, data, sizeof (data), MSG_PEEK); + error = WSAGetLastError (); + WSASetLastError (0); + + if (r > 0 || error == WSAENOTCONN) + happened |= (POLLIN | POLLRDNORM) & sought; + + /* Distinguish hung-up sockets from other errors. */ + else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET + || error == WSAECONNABORTED || error == WSAENETRESET) + happened |= POLLHUP; + + else + happened |= POLLERR; + } + + if (lNetworkEvents & (FD_WRITE | FD_CONNECT)) + happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; + + if (lNetworkEvents & FD_OOB) + happened |= (POLLPRI | POLLRDBAND) & sought; + + return happened; +} + +#else /* !MinGW */ + +/* Convert select(2) returned fd_sets into poll(2) revents values. */ +static int +compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds) +{ + int happened = 0; + if (FD_ISSET (fd, rfds)) + { + int r; + int socket_errno; + +# if defined __MACH__ && defined __APPLE__ + /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK + for some kinds of descriptors. Detect if this descriptor is a + connected socket, a server socket, or something else using a + 0-byte recv, and use ioctl(2) to detect POLLHUP. */ + r = recv (fd, NULL, 0, MSG_PEEK); + socket_errno = (r < 0) ? errno : 0; + if (r == 0 || socket_errno == ENOTSOCK) + ioctl (fd, FIONREAD, &r); +# else + char data[64]; + r = recv (fd, data, sizeof (data), MSG_PEEK); + socket_errno = (r < 0) ? errno : 0; +# endif + if (r == 0) + happened |= POLLHUP; + + /* If the event happened on an unconnected server socket, + that's fine. */ + else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN)) + happened |= (POLLIN | POLLRDNORM) & sought; + + /* Distinguish hung-up sockets from other errors. */ + else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET + || socket_errno == ECONNABORTED || socket_errno == ENETRESET) + happened |= POLLHUP; + + else + happened |= POLLERR; + } + + if (FD_ISSET (fd, wfds)) + happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; + + if (FD_ISSET (fd, efds)) + happened |= (POLLPRI | POLLRDBAND) & sought; + + return happened; +} +#endif /* !MinGW */ + +int +poll (pfd, nfd, timeout) + struct pollfd *pfd; + nfds_t nfd; + int timeout; +{ +#ifndef WIN32_NATIVE + fd_set rfds, wfds, efds; + struct timeval tv; + struct timeval *ptv; + int maxfd, rc; + nfds_t i; + +# ifdef _SC_OPEN_MAX + static int sc_open_max = -1; + + if (nfd < 0 + || (nfd > sc_open_max + && (sc_open_max != -1 + || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX))))) + { + errno = EINVAL; + return -1; + } +# else /* !_SC_OPEN_MAX */ +# ifdef OPEN_MAX + if (nfd < 0 || nfd > OPEN_MAX) + { + errno = EINVAL; + return -1; + } +# endif /* OPEN_MAX -- else, no check is needed */ +# endif /* !_SC_OPEN_MAX */ + + /* EFAULT is not necessary to implement, but let's do it in the + simplest case. */ + if (!pfd) + { + errno = EFAULT; + return -1; + } + + /* convert timeout number into a timeval structure */ + if (timeout == 0) + { + ptv = &tv; + ptv->tv_sec = 0; + ptv->tv_usec = 0; + } + else if (timeout > 0) + { + ptv = &tv; + ptv->tv_sec = timeout / 1000; + ptv->tv_usec = (timeout % 1000) * 1000; + } + else if (timeout == INFTIM) + /* wait forever */ + ptv = NULL; + else + { + errno = EINVAL; + return -1; + } + + /* create fd sets and determine max fd */ + maxfd = -1; + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&efds); + for (i = 0; i < nfd; i++) + { + if (pfd[i].fd < 0) + continue; + + if (pfd[i].events & (POLLIN | POLLRDNORM)) + FD_SET (pfd[i].fd, &rfds); + + /* see select(2): "the only exceptional condition detectable + is out-of-band data received on a socket", hence we push + POLLWRBAND events onto wfds instead of efds. */ + if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) + FD_SET (pfd[i].fd, &wfds); + if (pfd[i].events & (POLLPRI | POLLRDBAND)) + FD_SET (pfd[i].fd, &efds); + if (pfd[i].fd >= maxfd + && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI + | POLLRDNORM | POLLRDBAND + | POLLWRNORM | POLLWRBAND))) + { + maxfd = pfd[i].fd; + if (maxfd > FD_SETSIZE) + { + errno = EOVERFLOW; + return -1; + } + } + } + + /* examine fd sets */ + rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv); + if (rc < 0) + return rc; + + /* establish results */ + rc = 0; + for (i = 0; i < nfd; i++) + if (pfd[i].fd < 0) + pfd[i].revents = 0; + else + { + int happened = compute_revents (pfd[i].fd, pfd[i].events, + &rfds, &wfds, &efds); + if (happened) + { + pfd[i].revents = happened; + rc++; + } + } + + return rc; +#else + static struct timeval tv0; + static HANDLE hEvent; + WSANETWORKEVENTS ev; + HANDLE h, handle_array[FD_SETSIZE + 2]; + DWORD ret, wait_timeout, nhandles; + fd_set rfds, wfds, xfds; + BOOL poll_again; + MSG msg; + int rc = 0; + nfds_t i; + + if (nfd < 0 || timeout < -1) + { + errno = EINVAL; + return -1; + } + + if (!hEvent) + hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + + handle_array[0] = hEvent; + nhandles = 1; + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&xfds); + + /* Classify socket handles and create fd sets. */ + for (i = 0; i < nfd; i++) + { + int sought = pfd[i].events; + pfd[i].revents = 0; + if (pfd[i].fd < 0) + continue; + if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND + | POLLPRI | POLLRDBAND))) + continue; + + h = (HANDLE) _get_osfhandle (pfd[i].fd); + assert (h != NULL); + if (IsSocketHandle (h)) + { + int requested = FD_CLOSE; + + /* see above; socket handles are mapped onto select. */ + if (sought & (POLLIN | POLLRDNORM)) + { + requested |= FD_READ | FD_ACCEPT; + FD_SET ((SOCKET) h, &rfds); + } + if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND)) + { + requested |= FD_WRITE | FD_CONNECT; + FD_SET ((SOCKET) h, &wfds); + } + if (sought & (POLLPRI | POLLRDBAND)) + { + requested |= FD_OOB; + FD_SET ((SOCKET) h, &xfds); + } + + if (requested) + WSAEventSelect ((SOCKET) h, hEvent, requested); + } + else + { + /* Poll now. If we get an event, do not poll again. Also, + screen buffer handles are waitable, and they'll block until + a character is available. win32_compute_revents eliminates + bits for the "wrong" direction. */ + pfd[i].revents = win32_compute_revents (h, &sought); + if (sought) + handle_array[nhandles++] = h; + if (pfd[i].revents) + timeout = 0; + } + } + + if (select (0, &rfds, &wfds, &xfds, &tv0) > 0) + { + /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but + no need to call select again. */ + poll_again = FALSE; + wait_timeout = 0; + } + else + { + poll_again = TRUE; + if (timeout == INFTIM) + wait_timeout = INFINITE; + else + wait_timeout = timeout; + } + + for (;;) + { + ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, + wait_timeout, QS_ALLINPUT); + + if (ret == WAIT_OBJECT_0 + nhandles) + { + /* new input of some other kind */ + BOOL bRet; + while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + else + break; + } + + if (poll_again) + select (0, &rfds, &wfds, &xfds, &tv0); + + /* Place a sentinel at the end of the array. */ + handle_array[nhandles] = NULL; + nhandles = 1; + for (i = 0; i < nfd; i++) + { + int happened; + + if (pfd[i].fd < 0) + continue; + if (!(pfd[i].events & (POLLIN | POLLRDNORM | + POLLOUT | POLLWRNORM | POLLWRBAND))) + continue; + + h = (HANDLE) _get_osfhandle (pfd[i].fd); + if (h != handle_array[nhandles]) + { + /* It's a socket. */ + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + WSAEventSelect ((SOCKET) h, 0, 0); + + /* If we're lucky, WSAEnumNetworkEvents already provided a way + to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */ + if (FD_ISSET ((SOCKET) h, &rfds) + && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT))) + ev.lNetworkEvents |= FD_READ | FD_ACCEPT; + if (FD_ISSET ((SOCKET) h, &wfds)) + ev.lNetworkEvents |= FD_WRITE | FD_CONNECT; + if (FD_ISSET ((SOCKET) h, &xfds)) + ev.lNetworkEvents |= FD_OOB; + + happened = win32_compute_revents_socket ((SOCKET) h, pfd[i].events, + ev.lNetworkEvents); + } + else + { + /* Not a socket. */ + int sought = pfd[i].events; + happened = win32_compute_revents (h, &sought); + nhandles++; + } + + if ((pfd[i].revents |= happened) != 0) + rc++; + } + + return rc; +#endif +} diff --git a/compat/win32/sys/poll.h b/compat/win32/sys/poll.h new file mode 100644 index 0000000000..b7aa59d973 --- /dev/null +++ b/compat/win32/sys/poll.h @@ -0,0 +1,53 @@ +/* Header for poll(2) emulation + Contributed by Paolo Bonzini. + + Copyright 2001, 2002, 2003, 2007, 2009, 2010 Free Software Foundation, Inc. + + This file is part of gnulib. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_POLL_H +#define _GL_POLL_H + +/* fake a poll(2) environment */ +#define POLLIN 0x0001 /* any readable data available */ +#define POLLPRI 0x0002 /* OOB/Urgent readable data */ +#define POLLOUT 0x0004 /* file descriptor is writeable */ +#define POLLERR 0x0008 /* some poll error occurred */ +#define POLLHUP 0x0010 /* file descriptor was "hung up" */ +#define POLLNVAL 0x0020 /* requested events "invalid" */ +#define POLLRDNORM 0x0040 +#define POLLRDBAND 0x0080 +#define POLLWRNORM 0x0100 +#define POLLWRBAND 0x0200 + +struct pollfd +{ + int fd; /* which file descriptor to poll */ + short events; /* events we are interested in */ + short revents; /* events found on return */ +}; + +typedef unsigned long nfds_t; + +extern int poll (struct pollfd *pfd, nfds_t nfd, int timeout); + +/* Define INFTIM only if doing so conforms to POSIX. */ +#if !defined (_POSIX_C_SOURCE) && !defined (_XOPEN_SOURCE) +#define INFTIM (-1) +#endif + +#endif /* _GL_POLL_H */ -- cgit v1.2.3 From fdc12114635581b6901940ddb467a78716bc12ca Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 4 Nov 2010 02:35:21 +0100 Subject: mingw: use poll-emulation from gnulib Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- compat/mingw.c | 65 ------------------------------------------------- compat/mingw.h | 11 --------- compat/win32/sys/poll.c | 3 +-- 3 files changed, 1 insertion(+), 78 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index a6c1ee702a..29f403649f 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -408,71 +408,6 @@ int pipe(int filedes[2]) return 0; } -int poll(struct pollfd *ufds, unsigned int nfds, int timeout) -{ - int i, pending; - - if (timeout >= 0) { - if (nfds == 0) { - Sleep(timeout); - return 0; - } - return errno = EINVAL, error("poll timeout not supported"); - } - - /* When there is only one fd to wait for, then we pretend that - * input is available and let the actual wait happen when the - * caller invokes read(). - */ - if (nfds == 1) { - if (!(ufds[0].events & POLLIN)) - return errno = EINVAL, error("POLLIN not set"); - ufds[0].revents = POLLIN; - return 0; - } - -repeat: - pending = 0; - for (i = 0; i < nfds; i++) { - DWORD avail = 0; - HANDLE h = (HANDLE) _get_osfhandle(ufds[i].fd); - if (h == INVALID_HANDLE_VALUE) - return -1; /* errno was set */ - - if (!(ufds[i].events & POLLIN)) - return errno = EINVAL, error("POLLIN not set"); - - /* this emulation works only for pipes */ - if (!PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL)) { - int err = GetLastError(); - if (err == ERROR_BROKEN_PIPE) { - ufds[i].revents = POLLHUP; - pending++; - } else { - errno = EINVAL; - return error("PeekNamedPipe failed," - " GetLastError: %u", err); - } - } else if (avail) { - ufds[i].revents = POLLIN; - pending++; - } else - ufds[i].revents = 0; - } - if (!pending) { - /* The only times that we spin here is when the process - * that is connected through the pipes is waiting for - * its own input data to become available. But since - * the process (pack-objects) is itself CPU intensive, - * it will happily pick up the time slice that we are - * relinquishing here. - */ - Sleep(0); - goto repeat; - } - return 0; -} - struct tm *gmtime_r(const time_t *timep, struct tm *result) { /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */ diff --git a/compat/mingw.h b/compat/mingw.h index 51fca2f845..99a746703f 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -59,16 +59,6 @@ struct passwd { extern char *getpass(const char *prompt); -#ifndef POLLIN -struct pollfd { - int fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ -}; -#define POLLIN 1 -#define POLLHUP 2 -#endif - typedef void (__cdecl *sig_handler_t)(int); struct sigaction { sig_handler_t sa_handler; @@ -175,7 +165,6 @@ int pipe(int filedes[2]); unsigned int sleep (unsigned int seconds); int mkstemp(char *template); int gettimeofday(struct timeval *tv, void *tz); -int poll(struct pollfd *ufds, unsigned int nfds, int timeout); struct tm *gmtime_r(const time_t *timep, struct tm *result); struct tm *localtime_r(const time_t *timep, struct tm *result); int getpagesize(void); /* defined in MinGW's libgcc.a */ diff --git a/compat/win32/sys/poll.c b/compat/win32/sys/poll.c index 092d05e6bd..7e74ebe59a 100644 --- a/compat/win32/sys/poll.c +++ b/compat/win32/sys/poll.c @@ -24,8 +24,7 @@ # pragma GCC diagnostic ignored "-Wtype-limits" #endif -#include -#include +#include #include #include "poll.h" -- cgit v1.2.3